Practice Questions
1. What does the phrase “UI as a function of state” mean? Why is this concept important for understanding how React works?
Solution
It means that the user interface is determined entirely by the current state. Given the same state, the UI should always look the same. This matters because React is built around this idea: you describe what the UI should look like for a given state, and React updates the DOM whenever that state changes. You do not manually find and update DOM elements — you update state and let React re-render.
2. Explain the difference between building a UI with document.createElement / appendChild and using innerHTML with template strings. What are the trade-offs of each approach?
Solution
With document.createElement and appendChild, you build the DOM tree programmatically — creating each element, setting its attributes, and attaching it to a parent. This is explicit and gives you direct references to elements, but it is verbose and hard to read as the UI grows.
With innerHTML and template strings, you write the HTML structure as a string and assign it to an element’s innerHTML. This is more readable and resembles the final HTML output, but you lose direct references to elements and need to query the DOM afterward (e.g., with getElementById). It also replaces the entire inner content, which can be wasteful if only a small part changed.
3. What is JSX? How does the following JSX expression get processed before the browser runs it?
const element = <h1 className="title">Hello</h1>;
Solution
JSX is a syntax extension for JavaScript that lets you write HTML-like markup inside your JavaScript code. It is not valid JavaScript — a build tool (like Vite with the React plugin) transforms it into function calls before sending it to the browser.
With the modern “automatic” JSX runtime (used by default in Vite + React), the expression above is compiled into something like:
import { jsx as _jsx } from "react/jsx-runtime";
const element = _jsx("h1", { className: "title", children: "Hello" });
In the older “classic” runtime, it would instead compile to React.createElement("h1", { className: "title" }, "Hello"). Either way, the result is a plain JavaScript object describing the element, which React uses to update the DOM.
4. What are JSX interpolation braces ({}) used for? Given const name = "Ada", write a component that renders a greeting paragraph containing the name and the number of characters in the name.
Solution
Curly braces in JSX let you embed any JavaScript expression inside the markup. They can contain variables, function calls, arithmetic, ternary expressions, and more.
export default function Greeting() {
const name = "Ada";
return (
<p>
Hello, {name}! Your name has {name.length} characters.
</p>
);
}
5. In a React component, event handlers are attached directly in JSX. Write a component that renders a button and logs "clicked" to the console when the button is pressed. Use the correct JSX syntax for the event handler.
Solution
export default function ClickLogger() {
function handleClick() {
console.log("clicked");
}
return <button onClick={handleClick}>Click me</button>;
}
Key details: the attribute is onClick (camelCase, not onclick), and you pass the function reference (handleClick), not a function call (handleClick()).
6. A student writes the following component, but the displayed number never changes when the button is clicked. Explain why and fix the code.
export default function Tracker() {
let value = 0;
function add() {
value = value + 1;
console.log(value);
}
return (
<div>
<p>{value}</p>
<button onClick={add}>Add</button>
</div>
);
}
Solution
The variable value changes in memory (you can see it in console.log), but React has no way of knowing it changed. React only re-renders a component when its state, managed through hooks like useState, is updated. A plain local variable is re-created every time the component renders, so changes to it are lost and never reflected in the UI.
The fix is to use useState:
import { useState } from "react";
export default function Tracker() {
const [value, setValue] = useState(0);
function add() {
setValue(value + 1);
}
return (
<div>
<p>{value}</p>
<button onClick={add}>Add</button>
</div>
);
}
7. Describe what useState returns and how you use it. Write a component with a text input that displays the current value of the input below it as the user types.
Solution
useState takes an initial value and returns an array with two elements: the current state value and a setter function to update it. You typically destructure them:
const [value, setValue] = useState(initialValue);
Calling the setter function (e.g., setValue(newValue)) updates the state and triggers a re-render, so the UI reflects the new value.
import { useState } from "react";
export default function Echo() {
const [text, setText] = useState("");
return (
<div>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<p>You typed: {text}</p>
</div>
);
}
8. Explain what useEffect does and the role of the dependency array. What is the difference between these three calls?
useEffect(() => {
/* A */
});
useEffect(() => {
/* B */
}, []);
useEffect(() => {
/* C */
}, [x, y]);
Solution
useEffect lets you run side effects: code that should happen in response to rendering or state changes, outside the normal render flow (e.g., logging, fetching data, updating the document title).
The dependency array controls when the effect runs:
- No array (
A): The effect runs after every render. - Empty array (
B): The effect runs only once, after the initial render (mount). - With dependencies (
C): The effect runs after the initial render and then again whenever any value in the array (xory) changes.
9. Write a component that manages a numeric state called score. Include buttons to increment and reset the score. Use useEffect to update the document title to "Score: X" whenever the score changes.
Solution
import { useState, useEffect } from "react";
export default function ScoreTracker() {
const [score, setScore] = useState(0);
useEffect(() => {
document.title = `Score: ${score}`;
}, [score]);
return (
<div>
<p>Score: {score}</p>
<button onClick={() => setScore(score + 1)}>+1</button>
<button onClick={() => setScore(0)}>Reset</button>
</div>
);
}
The useEffect depends on [score], so it runs whenever score changes. The handlers only update state, and the side effect is handled separately.
10. Explain the role of each of the following in a React + Vite project: index.html, main.jsx, and App.jsx. How does React connect to the DOM?
Solution
index.html: The single HTML page served to the browser. It contains a<div id="root">element (the mount point) and a<script>tag that loadsmain.jsx.main.jsx: The entry point for React. It callscreateRoot(document.getElementById("root"))to create a React root, then calls.render(<App />)to render the top-level component into that root. It may also wrap the app in<StrictMode>for development warnings.App.jsx: The top-level React component. It is a function that returns JSX describing the UI. All other components are typically nested insideApp.
React connects to the DOM through createRoot — once mounted, React owns the contents of the root <div> and manages all DOM updates from there.
11. Compare how you would handle a button click in vanilla JavaScript versus in React. What are the key differences in approach?
Solution
In vanilla JavaScript, you first create or select the button element from the DOM, then attach an event listener using addEventListener:
const btn = document.getElementById("myBtn");
btn.addEventListener("click", () => {
console.log("clicked");
});
In React, you attach the handler directly in JSX using the onClick attribute:
function MyComponent() {
return <button onClick={() => console.log("clicked")}>Click</button>;
}
Key differences:
- Location: In vanilla JS, event wiring is separate from the element creation. In React, the handler is declared right where the element is defined, making the connection between UI and behavior more explicit.
- DOM querying: Vanilla JS requires you to find the element first (e.g.,
getElementById). React does not — you reference functions directly in JSX. - Naming: React uses camelCase (
onClick) instead of lowercase (onclickoraddEventListener("click", ...)). - Re-rendering: In vanilla JS, the handler must manually update the DOM. In React, the handler updates state, and React re-renders the component automatically.