Welcome back! In the last lesson, you learned how to build reusable components and pass data to them using props. Now, you are ready to make your React app interactive. In this lesson, you will learn how to let users add new books to a reading list and see the list update right away. This is possible thanks to two important ideas in React: state and event handling.
State lets your app remember things, like which books are in your list or what the user has typed into a form. Event handling lets your app respond to user actions, such as clicking a button or submitting a form. By the end of this lesson, you will know how to use both to build a simple, interactive reading list.
In React, the key to making apps interactive is state. State represents values that change over time and that React needs to keep track of in order to re-render the UI correctly. Without state, your components would only be able to display static content.
Why not just use let?
You might wonder: why not write something like this?
The problem is that React won’t know this value has changed. React’s rendering system is built around the idea that when state updates, the component should re-render to reflect the change. If you use a plain variable, React has no way of detecting that change, so the UI will stay the same on screen.
Introducing useState
React solves this with the useState hook:
Hook: A hook is a special React function that lets you “hook into” internal features like state and lifecycle. All hooks start with use, e.g. useState, useEffect.
-
useState:This hook gives you two things:- A state variable (like
books,newTitle,newAuthor). - A setter function (like , , ) that updates the state.
- A state variable (like
Here’s what happens under the hood:
- On the first render, React uses the initial state you provide.
- When you call a setter like
setBooks, React:- Updates the state internally.
- Triggers a re-render of the component.
- On the next render, the component sees the new state values and updates the UI accordingly.
Because React re-renders every time state changes, the UI always stays in sync with the underlying data. This is the core of React’s “declarative” model: you describe what the UI should look like for a given state, and React ensures the screen matches that description.
- State is for values that change over time and affect what gets rendered.
- You can’t just use
let— React needs to know when to re-render. - Hooks are special functions that give functional components extra capabilities.
- Initial state is the starting value you pass to useState; React preserves and updates it between renders.
With useState, your components can evolve from static templates into living, interactive UI elements.
Now, let’s see how you can let users add new books to the list. This is done with a form and an event handler.
Here’s the relevant part of the code:
Let’s walk through what happens:
- The form has two input fields: one for the book title and one for the author.
- Each input’s value is controlled by state (
newTitleandnewAuthor). When the user types, theonChangeevent updates the state. - When the form is submitted, the
handleAddBookfunction runs.e.preventDefault()stops the page from reloading.- If either field is empty, nothing happens.
- Otherwise, a new book is added to the
booksarray usingsetBooks. - The input fields are cleared by setting
newTitleandnewAuthorback to empty strings.
Example Output:
If you type "1984" as the title and "George Orwell" as the author, then click "Add Book," the new book will appear in your list.
Why onChange={(e) => setNewTitle(e.target.value)}
- Inputs are controlled components here: the
valuecomes from state, andonChangeupdates state. This keeps a single source of truth (state), ensuring the UI always mirrors state. onChange={(e) => ...}creates an inline handler that receives the synthetic evente, then reads the current input text frome.target.valueand saves it to state viasetNewTitle.- You could also define a named handler:
Why e.preventDefault()?
- HTML forms submit to the server and reload the page by default. In a SPA (Single Page Application), we prevent the default to keep the user on the same page and handle the submission entirely in JavaScript (updating React state instead).
Important: Why
onSubmit={handleAddBook}and notonSubmit={handleAddBook()}?
- In
JSX, passes a function reference to React. React will call it later when the submit event occurs.
Now, let’s see how the list of books is displayed and how it updates when you add a new book.
Here’s the code that renders the book list:
- If there are no books, a message is shown.
- If there are books, each one is displayed using the
BookCardcomponent. - When you add a new book, the
booksstate changes, and React automatically re-renders the list to include the new book.
Breaking It Down
- Ternary operator (
? :): We use the ternary operator (covered earlier) to choose between two outputs:- If
books.length === 0, show a fallback message. - Otherwise, show a grid of BookCard components.
- If
mapfunction: Themapmethod loops through thebooksarray and transforms each book object into aBookCardelement. This is a common React pattern: usemapto turn data arrays into lists of components.- Keys in lists:
Each
BookCardgets a . Keys help React efficiently update lists by identifying which items changed, were added, or removed.
The DOM (Document Object Model) is the browser’s way of representing a webpage.
Think of it as a tree structure of everything on the page:
- The
<h1>is one branch of the tree. - The
<p>is another branch. - When you change something (like updating text), the browser updates this tree and the screen.
Changing the DOM directly can be slow if you do it often, because the browser has to recalculate styles, re-layout the page, and repaint pixels.
One of React’s biggest performance advantages is how it handles the DOM (Document Object Model):
-
Traditional DOM: In plain JavaScript, if something changes, you often re-render or replace large parts of the DOM manually. This can be slow because the browser has to recalculate layout, repaint, etc.
-
React doesn’t touch the real DOM immediately. Instead, it uses something called the Virtual DOM. React keeps a lightweight “virtual” representation of the DOM in memory.
- When state changes, React creates a new virtual DOM tree.
- React compares (or diffs) the old virtual tree with the new one to see exactly what changed.
- Finally, it applies only the minimal updates needed to the real DOM.
This process is why React feels fast: the browser only updates the parts of the screen that truly changed.
If you add a new book to the list, React won’t redraw the whole page — it will only add one new
<BookCard>to the DOM. This makes updates feel fast and efficient.
When rendering arrays (like books.map(...)), React uses the key prop to track each element between renders:
- If a
BookCardhas the samekeyacross renders, React reuses the existing DOM node. - If the
keychanges, React removes the old node and creates a new one. - Stable keys (like IDs) mean fewer unnecessary removals and insertions, leading to smoother UI updates.
Rule of thumb: Always use a stable
idfrom your data as thekeywhenever possible. Use array indices only as a fallback.
In this lesson, you learned how to make your React app interactive by managing state and handling events. You saw how to use the useState hook to keep track of the book list and form inputs, and how to handle form submissions to add new books. You also saw how React updates the UI automatically when state changes.
Next, you will get a chance to practice these skills by working through hands-on exercises. You will use state and event handling to build your own interactive features. Good luck, and have fun experimenting!
