Forms
Handling form submission
jsx
<form onSubmit={handleAddMovie}>
<input value={title} onChange={event => setTitle(event.target.value)} placeholder="Movie title" />
<input value={genre} onChange={event => setGenre(event.target.value)} placeholder="Genre" />
<button type="submit">Add Movie</button>
</form>jsx
function handleAddMovie(event) {
event.preventDefault()
// ...
}- A
<form>'s default behavior on submit is to reload the page.event.preventDefault()stops that, so the submit is handled entirely in JS instead. type="submit"on the button is what makes clicking it, or pressing Enter inside one of the inputs, actually trigger the form'sonSubmit.- Both inputs are controlled the same way as the search box in Filtering,
value+onChange, just with their own state (title,genre) instead ofsearch.
Validating before adding
jsx
if (!title.trim() || !genre.trim()) {
setError("Please enter both title and genre")
return
}.trim()strips whitespace, so" "doesn't count as a real title.!title.trim()is true for an empty, or whitespace-only, string.- Either field failing sets an error message in state and
returns early. The rest ofhandleAddMovienever runs, no movie gets added.
Conditional rendering with &&
jsx
{error && <p>{error}</p>}errorempty (falsy) → the whole expression short-circuits to"", React renders nothing.errorhas text (truthy) → the expression evaluates to the<p>, and that's what renders.- This is the standard React way to say "render this, but only if that's true", no
ifstatement needed inside JSX.
Adding a new item to the array
jsx
const newMovie = {
id: Date.now(),
title: title.trim(),
genre: genre.trim(),
rating: 0,
watched: false
}
setMovies([...movies, newMovie])Date.now()here is just a quick way to get an id that's very unlikely to collide, the current time in milliseconds.[...movies, newMovie]is the "add" version of the same rule from State: never mutate the array directly,movies.push(newMovie)would be a mutation, build a new array instead. Spreadingmoviescopies all the existing items in, thennewMovielands after them.
Resetting the form after a successful add
jsx
setTitle("")
setGenre("")
setError("")- Three separate pieces of state, three separate resets. Since the inputs are controlled, setting
titleandgenreback to""immediately clears the boxes on screen too, there's no separate "clear the input" step. - Clearing
errormatters just as much here: if a previous attempt had failed and left a message behind, a successful add should make it disappear too.
Clamping a value with disabled buttons
jsx
<button disabled={movie.rating <= 0} onClick={() => onRating(movie.id, movie.rating - 1)}>-</button>
<button disabled={movie.rating >= 5} onClick={() => onRating(movie.id, movie.rating + 1)}>+</button>disabledtakes a boolean straight from the current state, no separate tracking needed for "can this be clicked". Onceratinghits0, the-button disables itself on the very next render, same idea for+at5.handleRatingis the exact same update pattern ashandleWatchedback in State,movies.map(...)with a ternary, just overwritingratinginstead ofwatched.