React Lecture
Build a UCLA Monopoly game step by step — covering JSX, the map function, components & props, state with useState, and the Virtual DOM.
JSX — JavaScript XML
React and JSX
React is a free, open-source front-end JavaScript library maintained by Meta (formerly Facebook) for building user interfaces.
React has a declarative nature: you describe what you want the UI to look like, and React handles the steps to achieve that UI and keep it updated efficiently when state changes. This is in contrast to imperative programming (plain JavaScript), which focuses on how to achieve a result by manually manipulating UI elements.
JSX — JavaScript XML
JSX is a syntax extension that allows developers to write HTML-like code within JavaScript. This makes it easier to define the structure and content of UI components.
const propertyName = "Royce Hall";
// [...]
return <h1>Welcome to {propertyName}!</h1>;
- Use curly braces
{ }to embed any JavaScript expression inside JSX - Babel compiles this JSX to plain JavaScript:
React.createElement('h1', null, 'Welcome to ', propertyName, '!') - The result is a lightweight Virtual DOM object — not a real DOM node yet
Task
The property name and price are hard-coded strings. Create variables propertyName and price, then use { } curly braces to embed them in the JSX instead.
// JSX: write HTML-like syntax directly in JavaScript
// Use { } curly braces to embed JavaScript expressions
const gameName = "UCLA Monopoly";
// TODO: Create a propertyName variable set to "Royce Hall"
// TODO: Create a price variable set to 1000
// TODO: Replace the hard-coded text below with {propertyName} and ${price}
function App() {
return (
<div style={{ padding: '16px', fontFamily: "'Georgia', serif" }}>
<h1>Welcome to UCLA Monopoly!</h1>
<p>Most expensive property: Royce Hall</p>
<p>Price: $1000</p>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
JSX — Knowledge Check
Min. score: 80%
1. What does Babel compile <h1>Hello, {name}!</h1> into?
JSX is syntactic sugar. Babel transforms it to React.createElement(type, props, ...children) calls, which return plain JavaScript objects — the Virtual DOM. No real DOM nodes are created at this stage. React’s reconciler does that later, applying only the minimal set of real DOM changes needed.
2. What does the { } curly-brace syntax do inside JSX?
In JSX, { expression } switches from JSX mode to JavaScript mode. Any valid JavaScript expression can appear inside the braces: variables, function calls, ternary operators, and so on. The result is converted to a string (or rendered as a child element) and inserted into the Virtual DOM.
3. React has a declarative nature. What does this mean?
Declarative programming means describing the desired outcome (what the UI should look like), not the procedure to get there. React compares the new description with the previous one (via the Virtual DOM) and applies only the minimal set of real DOM changes needed. This is the opposite of imperative manipulation like element.textContent = ....
The Map Function — Rendering Lists
Rendering Lists with .map()
In React, you render lists by using JavaScript’s built-in .map() function directly inside JSX.
const properties = [
{ name: "Royce Hall", price: 1000 },
{ name: "Powell Library", price: 500 },
];
// [...]
return (<ul>
{properties.map((prop, index) => (
<li key={index}>{prop.name} — ${prop.price}</li>
))}
</ul>);
properties.map(...)returns an array of JSX elements — React renders all of them as children of<ul>- The
keyprop is required on each list element so React can efficiently track which items changed, were added, or were removed keymust be unique within the list — here we use the array index, but in production prefer a stable ID from your data
Task
The properties array is defined but the list is empty. Use properties.map() to render each property as a <li> element showing its name and price. Don’t forget the key prop!
function App() {
const properties = [
{ name: "Royce Hall", price: 1000 },
{ name: "Powell Library", price: 500 },
{ name: "Kerckhoff Hall", price: 60 },
];
return (
<div style={{ padding: '16px', fontFamily: "'Georgia', serif" }}>
<h2>UCLA Monopoly — Property Listing</h2>
<ul>
{/* TODO: Use properties.map() to render each property */}
{/* Each <li> should show: name — $price */}
{/* Don't forget the key prop! */}
</ul>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
Map Function — Knowledge Check
Min. score: 80%
1. Why does React require a key prop on each element rendered inside .map()?
React’s reconciler uses key to match elements between renders. Without a stable key, React must assume every item may have changed and re-renders all of them. With a key, it can skip unchanged items. Using the array index as key is fine for static lists, but use a stable unique ID for lists that can be reordered or filtered.
2. A student writes this to render the property list:
return (
<ul>
{properties.map(prop => <li>{prop.name}</li>)}
</ul>
);
React warns: Each child in a list should have a unique “key” prop. The fix: <li key={index}>{prop.name}</li> or, better, <li key={prop.id}>{prop.name}</li> using a stable ID. Without keys, React cannot efficiently reconcile list changes and may produce incorrect rendering during updates.
3. What does properties.map((prop, index) => <li key={index}>{prop.name}</li>) return?
.map() always returns an array. When JSX encounters an array of React elements as children, it renders each one in order. So {properties.map(...)} is equivalent to listing each <li> element individually in the JSX — .map() just generates them programmatically from the data.
Components & Props — Monopoly Properties
Components & Props
Components are self-contained, reusable pieces of UI. In our UCLA Monopoly game, every property card has the same structure — a colored banner, a name, and a price — but the data differs.
The Problem: Hard-Coded Cards
The current code has a single hard-coded card for Royce Hall. Want three cards? You’d have to copy-paste and change every value by hand.
The Solution: A Reusable Component with Props
Props (short for properties) are read-only inputs passed to a component — just like function arguments. Extract the card into a component and make the varying parts props:
const MonopolyProperty = ({ name, price, colorGroup }) => {
return (
<div style={cardStyle}>
<div style={getHeaderStyle(colorGroup)}></div>
<div style={bodyStyle}>
<h4>{name}</h4>
<p><strong>${price}</strong></p>
</div>
</div>
);
};
Now rendering three properties is clean — same component, different data:
<MonopolyProperty name="Royce Hall" price={1000} colorGroup="ucla-blue" />
<MonopolyProperty name="Powell Library" price={500} colorGroup="ucla-blue" />
{ name, price, colorGroup }destructures the props object- Props are read-only — a component must never modify its own props
- The styles live in
styles.js— the component only handles structure and data
Task
Extract a reusable MonopolyProperty component that takes name, price, and colorGroup as props. Then render three different UCLA property cards side by side.
// Monopoly color groups — maps a group name to its banner color
const colorGroups = {
"ucla-blue": "#2774AE",
"ucla-gold": "#FFD100",
};
// Shared card styles for every property tile
const cardStyle = {
width: "180px",
border: "3px solid #333",
borderRadius: "12px",
overflow: "hidden",
margin: "8px",
fontFamily: "'Georgia', serif",
background: "#F5F0E1",
color: "#333",
boxShadow: "2px 4px 10px rgba(0,0,0,0.2)",
};
// Returns the colored header banner for a given color group
function getHeaderStyle(colorGroup) {
return {
height: "45px",
background: colorGroups[colorGroup] || "#ccc",
};
}
const bodyStyle = {
padding: "12px",
textAlign: "center",
};
// This card is hard-coded for Royce Hall — NOT reusable!
// TODO: Extract a MonopolyProperty component with props:
// name, price, colorGroup
// TODO: Render three property cards with different data
function App() {
return (
<div style={{ display: 'flex', flexWrap: 'wrap', padding: '16px' }}>
<div style={cardStyle}>
<div style={getHeaderStyle("ucla-blue")}></div>
<div style={bodyStyle}>
<h4>Royce Hall</h4>
<p><strong>$1000</strong></p>
</div>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
Components & Props — Knowledge Check
Min. score: 80%1. What are props in a React component?
Props (short for properties) are read-only inputs passed from a parent to a child, just like arguments to a function. The MonopolyProperty component takes name, price, and colorGroup as props — this lets the same component render Royce Hall, Powell Library, or any other property without duplicating code.
2. Why must React component names start with a capital letter?
In JSX, <card /> is treated as an unknown HTML element, while <Card /> calls your Card function. If you wrote <monopolyProperty /> in lowercase, React would try to create an HTML element called monopolyproperty — nothing would render.
3. A component is defined as:
const MonopolyProperty = ({ name, price, colorGroup }) => { ... };
{ name, price, colorGroup } in the parameter list do?
{ name, price, colorGroup } is JavaScript object destructuring applied to the props parameter. It is equivalent to writing (props) => { const name = props.name; ... }. Destructuring is idiomatic React — it makes the expected props explicit and keeps the component body concise.
4. Evaluate this code:
function MonopolyProperty(props) {
props.price = props.price * 2; // double the price
return <p>{props.price}</p>;
}
Props are immutable from the receiving component’s perspective. Mutating props breaks React’s unidirectional data flow — the parent still holds the original value, and the mutation is invisible outside this component. To track values that change over time, use state (useState) — that is the next step.
State & useState — Buying Houses
State — Components That Remember
Props are read-only inputs. But what if a component needs to track something that changes over time? In Monopoly, players buy houses to upgrade their properties — the number of houses changes with each purchase.
React provides the useState hook:
const [houses, setHouses] = useState(0);
// ↑ current value ↑ setter ↑ initial value
useState(0)returns a pair:[currentValue, setter]- Calling the setter —
setHouses(houses + 1)— updates the value and triggers a re-render - When
housesreaches 5, the houses upgrade to a hotel!
Event Handlers
A button triggers buyHouse when clicked:
const buyHouse = () => {
setHouses(houses + 1);
};
// ...
<button onClick={buyHouse}>Buy House</button>
onClick={buyHouse} passes the function reference — React calls it when the user clicks. Do not write onClick={buyHouse()} — that calls the function immediately during render.
Visual Feedback
The renderBuildings(houses) helper converts the house count into icons:
- 0 houses → “No buildings”
- 1–4 houses → house icons
- 5 houses → hotel icon (upgrade!)
Task
The property cards are static — clicking the button does nothing. Add a [houses, setHouses] state variable with useState(0), implement the buyHouse handler that increments houses up to MAX_HOUSES + 1, and display the buildings with renderBuildings(houses).
// Monopoly color groups
const colorGroups = {
"ucla-blue": "#2774AE",
"ucla-gold": "#FFD100",
};
const cardStyle = {
width: "200px",
border: "3px solid #333",
borderRadius: "12px",
overflow: "hidden",
margin: "8px",
fontFamily: "'Georgia', serif",
background: "#F5F0E1",
color: "#333",
boxShadow: "2px 4px 10px rgba(0,0,0,0.2)",
};
function getHeaderStyle(colorGroup) {
return {
height: "45px",
background: colorGroups[colorGroup] || "#ccc",
};
}
const bodyStyle = {
padding: "12px",
textAlign: "center",
};
const buttonStyle = {
padding: "6px 14px",
border: "2px solid #333",
borderRadius: "6px",
background: "#4CAF50",
color: "white",
fontWeight: "bold",
cursor: "pointer",
marginTop: "8px",
fontSize: "13px",
};
// Building icons
const HOUSE_EMOJI = "\uD83C\uDFE0";
const HOTEL_EMOJI = "\uD83C\uDFE8";
const { useState } = React;
const MAX_HOUSES = 4;
// Converts house count to visual building icons
function renderBuildings(houses) {
if (houses === 0) return "No buildings";
if (houses > MAX_HOUSES) return HOTEL_EMOJI;
return HOUSE_EMOJI.repeat(houses);
}
// TODO: Add state: const [houses, setHouses] = useState(0);
// TODO: Add buyHouse handler that increments houses (up to MAX_HOUSES + 1)
// TODO: Use renderBuildings(houses) to display buildings
// TODO: Wire up onClick={buyHouse} on the button
const MonopolyProperty = ({ name, price, colorGroup }) => {
return (
<div style={cardStyle}>
<div style={getHeaderStyle(colorGroup)}></div>
<div style={bodyStyle}>
<h4>{name}</h4>
<p><strong>${price}</strong></p>
<div style={{ fontSize: "24px", minHeight: "36px" }}>
No buildings
</div>
<button style={buttonStyle}>
Buy House ($50)
</button>
</div>
</div>
);
};
function App() {
return (
<div style={{ display: 'flex', flexWrap: 'wrap', padding: '16px' }}>
<MonopolyProperty name="Royce Hall" price={1000} colorGroup="ucla-blue" />
<MonopolyProperty name="Powell Library" price={500} colorGroup="ucla-blue" />
<MonopolyProperty name="Kerckhoff Hall" price={60} colorGroup="ucla-gold" />
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
State & useState — Knowledge Check
Min. score: 80%
1. What does const [houses, setHouses] = useState(0) do?
useState(0) returns [currentValue, setter]. Destructuring binds them to houses and setHouses. When setHouses(newValue) is called, React re-runs the component with the new houses value, builds a new Virtual DOM, diffs it, and patches only what changed — like adding a new house icon.
2. A student writes <button onClick={buyHouse()}>. What is wrong?
onClick={buyHouse()} evaluates buyHouse() during render — it runs immediately and passes its return value (probably undefined) to onClick. onClick={buyHouse} passes the function reference — React calls it only when the user actually clicks the button.
3. Two <MonopolyProperty> cards are on the board. A player buys 3 houses on Royce Hall. What happens to Powell Library’s houses?
Each component instance has its own isolated state. The houses in Royce Hall’s card has no connection to the houses in Powell Library’s card. This isolation is a key benefit of component-based UI — each property card is self-contained and predictable.
4. Evaluate this code:
function MonopolyProperty({ name }) {
let houses = 0;
const buyHouse = () => { houses = houses + 1; };
return (
<>
<p>{name}: {houses} houses</p>
<button onClick={buyHouse}>Buy</button>
</>
);
}
React only re-renders when useState’s setter is called. A plain let variable is local to the function — incrementing it does not trigger a re-render, so the displayed house count never changes. And when React re-runs the function for any other reason, houses resets to 0. You must use useState.
Virtual DOM — Complete UCLA Monopoly
The Virtual DOM
React employs a Virtual DOM — a lightweight JavaScript representation of the actual DOM.
When data changes, React:
- First updates the Virtual DOM (fast — it is just a JavaScript object tree)
- Calculates the differences between the new and previous Virtual DOM
- Applies only the necessary changes to the real DOM
This minimizes direct DOM manipulation and improves performance.
The game below puts everything together: JSX, .map(), components, props, and useState. When you buy a property or build a house, React only updates the specific card and wallet — not the entire page. That is the Virtual DOM in action.
Separation of Concerns
Notice how the code is organized into three separate modules:
| Module | Layer | Responsibility |
|---|---|---|
monopoly-game.js |
Application | Game data, rules, and state transitions — pure JavaScript, no React |
PropertyCard.jsx |
Presentation | Renders a single property card from props — pure UI, no game logic |
App.jsx |
Wiring | Connects game logic to UI via React state |
This is the Separation of Concerns principle: each module has a single, well-defined responsibility. The game logic knows nothing about React or the DOM. The UI component knows nothing about game rules. Benefits:
- Testability —
monopoly-game.jscan be unit-tested without rendering any UI - Reusability —
PropertyCardcan display any property data, from any source - Maintainability — changing a game rule doesn’t require touching UI code, and vice versa
Single Page Application (SPA)
SPAs are structured as a single HTML page with no preloaded content. Content is loaded dynamically via JavaScript:
@startuml
participant c: Client
participant s: Server
c -> s: GET '/'
activate s
s --> c: Single Page App (contains all pages)
deactivate s
c -> s: GET /current_news
activate s
s --> c: JSON data
deactivate s
c -> s: GET /about
activate s
s --> c: JSON data
deactivate s
@enduml
React is the most common technology for building SPAs. Navigation happens client-side — no full page reloads.
Server-Side Rendering (SSR)
Frameworks like Next.js (which uses React) do SSR. Clients receive a fully rendered page from the server:
@startuml
participant c: Client
participant s: Server
c -> s: GET '/'
activate s
s --> c: Main page (fully rendered)
deactivate s
c -> s: GET /current_news
activate s
s --> c: Current news page
deactivate s
@enduml
| Aspect | SPA | SSR |
|---|---|---|
| Server compute | Low | High |
| Initial load time | Slow (JS must download first) | Fast (pre-rendered HTML) |
| User interaction | Fast (client-side) | Slower (server round-trip) |
| SEO | Harder | Better |
Task
The game is almost complete — you can buy properties and build houses. But the Collect Rent button doesn’t work yet. Implement collectRent in App.jsx so it adds totalRent to the wallet using setWallet. Notice how the game logic in monopoly-game.js already provides calculateTotalRent — your App.jsx only needs to call setWallet.
// Color group palette — maps abstract group names to banner colors
const colorGroups = {
"ucla-blue": "#2774AE",
"ucla-gold": "#FFD100",
};
function getHeaderStyle(colorGroup) {
return {
height: "40px",
background: colorGroups[colorGroup] || "#ccc",
};
}
const cardStyle = {
width: "180px",
border: "3px solid #333",
borderRadius: "12px",
overflow: "hidden",
margin: "8px",
fontFamily: "'Georgia', serif",
background: "#F5F0E1",
color: "#333",
boxShadow: "2px 4px 10px rgba(0,0,0,0.2)",
};
const bodyStyle = {
padding: "10px",
textAlign: "center",
fontSize: "14px",
};
const buttonStyle = {
padding: "5px 12px",
border: "2px solid #333",
borderRadius: "6px",
background: "#4CAF50",
color: "white",
fontWeight: "bold",
cursor: "pointer",
marginTop: "6px",
fontSize: "12px",
};
const HOUSE_EMOJI = "\uD83C\uDFE0";
const HOTEL_EMOJI = "\uD83C\uDFE8";
const HOUSE_COST = 50;
const walletBarStyle = {
display: "flex",
gap: "16px",
alignItems: "center",
background: "#F5F0E1",
color: "#333",
padding: "10px 16px",
borderRadius: "8px",
marginBottom: "16px",
border: "2px solid #333",
flexWrap: "wrap",
fontFamily: "'Georgia', serif",
};
// ── Application Layer ─────────────────────────────────────
// Pure game logic — no React, no JSX, no DOM.
// This module can be unit-tested without rendering any UI.
const MAX_HOUSES = 4;
const INITIAL_PROPERTIES = [
{ name: "Royce Hall", price: 400, street: "Westwood Plaza", rent: 50 },
{ name: "Powell Library", price: 350, street: "Westwood Plaza", rent: 35 },
{ name: "Kerckhoff Hall", price: 200, street: "Bruin Walk", rent: 25 },
{ name: "Janss Steps", price: 150, street: "Bruin Walk", rent: 20 },
{ name: "Bruin Bear", price: 100, street: "Westwood Plaza", rent: 10 },
{ name: "Sculpture Garden", price: 60, street: "Bruin Walk", rent: 6 },
];
// Create the initial game state from the property definitions
function createInitialGameState() {
return INITIAL_PROPERTIES.map(p => ({ ...p, owned: false, houses: 0 }));
}
// Game action: buy a property.
// Returns updated { properties, wallet } or null if invalid.
function handleBuy(properties, wallet, index) {
const prop = properties[index];
if (!prop.owned && wallet >= prop.price) {
const updated = [...properties];
updated[index] = { ...prop, owned: true };
return { properties: updated, wallet: wallet - prop.price };
}
return null;
}
// Game action: build a house on an owned property.
// Returns updated { properties, wallet } or null if invalid.
function handleBuildHouse(properties, wallet, index) {
const prop = properties[index];
if (prop.owned && prop.houses <= MAX_HOUSES && wallet >= HOUSE_COST) {
const updated = [...properties];
updated[index] = { ...prop, houses: prop.houses + 1 };
return { properties: updated, wallet: wallet - HOUSE_COST };
}
return null;
}
// Calculate current rent for a property (base rent + house bonus)
function currentRent(property) {
return property.rent + property.houses * 10;
}
// Is the property fully upgraded (hotel)?
function isFullyUpgraded(property) {
return property.houses > MAX_HOUSES;
}
// Can the player afford a given cost?
function canAfford(wallet, cost) {
return wallet >= cost;
}
// Count how many properties the player owns
function ownedCount(properties) {
return properties.filter(p => p.owned).length;
}
// Calculate total rent from all owned properties
function calculateTotalRent(properties) {
return properties
.filter(p => p.owned)
.reduce((sum, p) => sum + currentRent(p), 0);
}
// ── Presentation Layer ────────────────────────────────────
// Pure UI component — renders a property card from props.
// Contains no game logic; all actions come via callbacks.
// Maps street names (game data) to color groups (style data)
const streetToColorGroup = {
"Westwood Plaza": "ucla-blue",
"Bruin Walk": "ucla-gold",
};
// Converts house count to visual building icons
function renderBuildings(houses) {
if (houses === 0) return "No buildings";
if (houses > MAX_HOUSES) return HOTEL_EMOJI;
return HOUSE_EMOJI.repeat(houses);
}
function PropertyCard({ property, wallet, onBuy, onBuildHouse }) {
const { name, price, street, owned, houses } = property;
const isMaxed = isFullyUpgraded(property);
const rentNow = currentRent(property);
const canBuy = canAfford(wallet, price);
const canBuild = !isMaxed && canAfford(wallet, HOUSE_COST);
return (
<div style={cardStyle}>
<div style={getHeaderStyle(streetToColorGroup[street])}></div>
<div style={bodyStyle}>
<h4 style={{ margin: "4px 0" }}>{name}</h4>
<p style={{ margin: "2px 0" }}><strong>${price}</strong></p>
{owned ? (
<>
<p style={{ margin: "2px 0", fontSize: "11px", color: "#666" }}>
Rent: ${rentNow}
</p>
<div style={{ fontSize: "20px", minHeight: "28px" }}>
{renderBuildings(houses)}
</div>
<button
onClick={onBuildHouse}
disabled={!canBuild}
style={{
...buttonStyle,
...(!canBuild
? { background: "#999", cursor: "default" } : {}),
}}
>
{isMaxed ? "Fully Upgraded!" : "Build House ($" + HOUSE_COST + ")"}
</button>
</>
) : (
<button
onClick={onBuy}
disabled={!canBuy}
style={{
...buttonStyle,
background: canBuy ? "#2774AE" : "#999",
}}
>
Buy (${price})
</button>
)}
</div>
</div>
);
}
const { useState } = React;
// App wires the application layer (monopoly-game.js)
// to the presentation layer (PropertyCard.jsx) via React state.
function App() {
const [wallet, setWallet] = useState(1500);
const [properties, setProperties] = useState(createInitialGameState());
// Apply a game action result to React state
const applyAction = (result) => {
if (result) {
setWallet(result.wallet);
setProperties(result.properties);
}
};
const totalRent = calculateTotalRent(properties);
const collectRent = () => {
// TODO: Add totalRent to the wallet using setWallet
};
return (
<div style={{ padding: "16px", fontFamily: "'Georgia', serif" }}>
<h2 style={{ marginBottom: "4px" }}>UCLA Monopoly</h2>
<div style={walletBarStyle}>
<span><strong>Wallet:</strong> ${wallet}</span>
<span><strong>Properties:</strong> {ownedCount(properties)}/{properties.length}</span>
<span><strong>Rent/turn:</strong> ${totalRent}</span>
<button
onClick={collectRent}
disabled={totalRent === 0}
style={{
...buttonStyle,
background: totalRent > 0 ? "#2774AE" : "#999",
marginTop: 0,
}}
>
Collect Rent
</button>
</div>
<div style={{ display: "flex", flexWrap: "wrap" }}>
{properties.map((prop, index) => (
<PropertyCard
key={index}
property={prop}
wallet={wallet}
onBuy={() => applyAction(handleBuy(properties, wallet, index))}
onBuildHouse={() => applyAction(handleBuildHouse(properties, wallet, index))}
/>
))}
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
Virtual DOM, SPA & SSR — Knowledge Check
Min. score: 80%1. What is the Virtual DOM and why does React use it?
The Virtual DOM is a plain JavaScript object tree. When state changes, React builds a new Virtual DOM, diffs it against the previous one (reconciliation), and applies only the changed parts to the real DOM. Real DOM manipulation is slow; diffing JavaScript objects is fast. This lets React optimize updates automatically without developers manually tracking what changed.
2. What is the key difference between a Single Page Application (SPA) and Server-Side Rendering (SSR)?
SPAs ship a minimal HTML shell plus a JavaScript bundle; the browser runs the JS to build and update the UI. Navigation happens client-side — no full page reload. SSR pre-renders HTML on the server for each request; the browser gets ready-to-display HTML immediately. SPAs excel at interaction speed after load; SSR excels at initial load time and SEO. Frameworks like Next.js mix both per-route.
3. Which of the following is an advantage of SSR over SPA for a public news website?
For a public news site, SEO and fast initial load are critical. SSR pre-renders HTML with article content already present — search engine crawlers can index it immediately, and users see content before JavaScript loads. The trade-off is higher server compute. SPAs excel at interaction speed after the initial load, not initial load or SEO. This is why Next.js (SSR-capable) is popular for content-heavy sites.
4. In the UCLA Monopoly game, you click “Collect Rent” which calls setWallet(wallet + totalRent). What does React update in the real DOM?
When setWallet is called, React re-runs the App function with the new wallet value. It builds a new Virtual DOM tree, compares it with the previous one, and finds that only the wallet text changed. React patches just that one text node in the real DOM — the property cards and everything else remain untouched. This selective update is the core benefit of the Virtual DOM.
5. The UCLA Monopoly code separates monopoly-game.js (application layer) from PropertyCard.jsx (presentation layer). What is the main benefit of this separation?
Separation of concerns means each module has one job. monopoly-game.js contains pure functions — you can test gameBuyProperty() with plain assertions, no DOM needed. PropertyCard.jsx is a pure UI component — it renders whatever data it receives. If you change the rent formula, only monopoly-game.js changes. If you redesign the card layout, only PropertyCard.jsx changes. Neither module needs to know about the other’s internals.