0% found this document useful (0 votes)
2 views18 pages

Fullstack Part2 - Forms

The document outlines the process of expanding a React application to allow users to add new notes, utilizing the useState hook to manage component state. It details the implementation of a form for note submission, handling input changes through controlled components, and filtering displayed notes based on importance. Additionally, it introduces a simple phonebook application as part of exercises to further develop React skills.

Uploaded by

monye obed
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views18 pages

Fullstack Part2 - Forms

The document outlines the process of expanding a React application to allow users to add new notes, utilizing the useState hook to manage component state. It details the implementation of a form for note submission, handling input changes through controlled components, and filtering displayed notes based on importance. Additionally, it introduces a simple phonebook application as part of exercises to further develop React skills.

Uploaded by

monye obed
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

b

Forms
Let's continue expanding our application by allowing users to add new notes. You can find
the code for our current application here .

Saving the notes in the component state

To get our page to update when new notes are added it's best to store the notes in the App
component's state. Let's import the useState function and use it to define a piece of state
that gets initialized with the initial notes array passed in the props.

import { useState } from 'react' copy


import Note from './components/Note'

{()App=>= (fs}
const props) => {
const [notes, setNotes] = useState(props.notes)

return (
<div>

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 1 of 18
:
<h1>Notes</h1>
<ul>
{notes.map(note =>
<Note key={note.id} note={note} />
)}
</ul>
</div>
)
}

export default App

The component uses the useState function to initialize the piece of state stored in
notes with the array of notes passed in the props:

const App = (props) => { copy


const [notes, setNotes] = useState(props.notes)

// ...
}

We can also use React Developer Tools to see that this really happens:

If we wanted to start with an empty list of notes, we would set the initial value as an empty
array, and since the props would not be used, we could omit the props parameter from
the function definition:

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 2 of 18
:
const App = () => { copy
const [notes, setNotes] = useState([])

// ...
}

Let's stick with the initial value passed in the props for the time being.

Next, let's add an HTML form to the component that will be used for adding new notes.

const App = (props) => { copy


const [notes, setNotes] = useState(props.notes)

const addNote = (event) => {


event.preventDefault()
console.log('button clicked', event.target)
}

return (
<div>
<h1>Notes</h1>
<ul>
{notes.map(note =>
<Note key={note.id} note={note} />
)}
</ul>
<form onSubmit={addNote}>
<input />
<button type="submit">save</button>
</form>
</div>
)
}

We have added the addNote function as an event handler to the form element that will be
called when the form is submitted, by clicking the submit button.

We use the method discussed in part 1 for defining our event handler:

const addNote = (event) => { copy


event.preventDefault()
console.log('button clicked', event.target)
}

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 3 of 18
:
The event parameter is the event that triggers the call to the event handler function:

The event handler immediately calls the event.preventDefault() method, which


prevents the default action of submitting a form. The default action would, among other
things , cause the page to reload.

The target of the event stored in event.target is logged to the console:

The target in this case is the form that we have defined in our component.

How do we access the data contained in the form's input element?

Controlled component

There are many ways to accomplish this; the first method we will take a look at is through
the use of so-called controlled components .

Let's add a new piece of state called newNote for storing the user-submitted input and
let's set it as the input element's value attribute:

const App = (props) => { copy


const [notes, setNotes] = useState(props.notes)
const [newNote, setNewNote] = useState(
'a new note...'
)

const addNote = (event) => {


event.preventDefault()
console.log('button clicked', event.target)
}

return (
<div>

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 4 of 18
:
<h1>Notes</h1>
<ul>
{notes.map(note =>
<Note key={note.id} note={note} />
)}
</ul>
<form onSubmit={addNote}>
<input value={newNote} />
<button type="submit">save</button>
</form>
</div>
)
}

The placeholder text stored as the initial value of the newNote state appears in the input
element, but the input text can't be edited. The console displays a warning that gives us a
clue as to what might be wrong:

Since we assigned a piece of the App component's state as the value attribute of the input
element, the App component now controls the behavior of the input element.

To enable editing of the input element, we have to register an event handler that
synchronizes the changes made to the input with the component's state:

const App = (props) => { copy


const [notes, setNotes] = useState(props.notes)
const [newNote, setNewNote] = useState(
'a new note...'
)

// ...

const handleNoteChange = (event) => {


console.log(event.target.value)
setNewNote(event.target.value)
}

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 5 of 18
:
return (
<div>
<h1>Notes</h1>
<ul>
{notes.map(note =>
<Note key={note.id} note={note} />
)}
</ul>
<form onSubmit={addNote}>
<input
value={newNote}
onChange={handleNoteChange}
/>
<button type="submit">save</button>
</form>
</div>
)
}

We have now registered an event handler to the onChange attribute of the form's input
element:

<input copy
value={newNote}
onChange={handleNoteChange}
/>

The event handler is called every time a change occurs in the input element. The event
handler function receives the event object as its event parameter:

const handleNoteChange = (event) => { copy


console.log(event.target.value)
setNewNote(event.target.value)
}

The target property of the event object now corresponds to the controlled input element,
and event.target.value refers to the input value of that element.

Note that we did not need to call the event.preventDefault() method like we did in
the onSubmit event handler. This is because no default action occurs on an input change,

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 6 of 18
:
unlike a form submission.

You can follow along in the console to see how the event handler is called:

You did remember to install React devtools , right? Good. You can directly view how the
state changes from the React Devtools tab:

Now the App component's newNote state reflects the current value of the input, which
means that we can complete the addNote function for creating new notes:

const addNote = (event) => { copy

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 7 of 18
:
event.preventDefault()
const noteObject = {
content: newNote,
important: Math.random() < 0.5,
id: String(notes.length + 1),
}

setNotes(notes.concat(noteObject))
setNewNote('')
}

First, we create a new object for the note called noteObject that will receive its content
from the component's newNote state. The unique identifier id is generated based on the
total number of notes. This method works for our application since notes are never deleted.
With the help of the Math.random() function, our note has a 50% chance of being
marked as important.

The new note is added to the list of notes using the concat array method, introduced in part
1:

setNotes(notes.concat(noteObject)) copy

The method does not mutate the original notes array, but rather creates a new copy of the
array with the new item added to the end. This is important since we must never mutate
state directly in React!

The event handler also resets the value of the controlled input element by calling the
setNewNote function of the newNote state:

setNewNote('') copy

You can find the code for our current application in its entirety in the part2-2 branch of this
GitHub repository .

Filtering Displayed Elements

Let's add some new functionality to our application that allows us to only view the important
notes.

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 8 of 18
:
Let's add a piece of state to the App component that keeps track of which notes should be
displayed:

const App = (props) => { copy


const [notes, setNotes] = useState(props.notes)
const [newNote, setNewNote] = useState('')
const [showAll, setShowAll] = useState(true)

// ...
}

Let's change the component so that it stores a list of all the notes to be displayed in the
notesToShow variable. The items on the list depend on the state of the component:

import { useState } from 'react' copy


import Note from './components/Note'

const App = (props) => {


const [notes, setNotes] = useState(props.notes)
const [newNote, setNewNote] = useState('')
const [showAll, setShowAll] = useState(true)

// ...

const notesToShow = showAll


? notes
: notes.filter (note => note.important === true)

return (
<div>
<h1>Notes</h1>
<ul>
{notesToShow.map(note =>
<Note key={note.id} note={note} />
)}
</ul>
// ...
</div>
)
}

The definition of the notesToShow variable is rather compact:

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 9 of 18
:
const notesToShow = showAll copy
? notes
: notes.filter (note => note.important === true)

The definition uses the conditional operator also found in many other programming
languages.

The operator functions as follows. If we have:

const result = condition ? val1 : val2 copy

the result variable will be set to the value of val1 if condition is true. If
condition is false, the result variable will be set to the value of val2 .

If the value of showAll is false, the notesToShow variable will be assigned to a list that
only contains notes that have the important property set to true. Filtering is done with
the help of the array filter method:

notes.filter (note => note.important === true) copy

The comparison operator is redundant, since the value of note.important is either true
or false, which means that we can simply write:

notes.filter (note => note.important) copy

We showed the comparison operator first to emphasize an important detail: in JavaScript


val1 == val2 does not always work as expected. When performing comparisons, it's
therefore safer to exclusively use val1 === val2 . You can read more about the topic
here .

You can test out the filtering functionality by changing the initial value of the showAll
state.

Next, let's add functionality that enables users to toggle the showAll state of the
application from the user interface.

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 10 of 18
:
The relevant changes are shown below:

import { useState } from 'react' copy


import Note from './components/Note'

const App = (props) => {


const [notes, setNotes] = useState(props.notes)
const [newNote, setNewNote] = useState('')
const [showAll, setShowAll] = useState(true)

// ...

return (
<div>
<h1>Notes</h1>
<div>
<button onClick={() => setShowAll(!showAll)}>
show {showAll ? 'important' : 'all'}
</button>
</div>
<ul>
{notesToShow.map(note =>
<Note key={note.id} note={note} />
)}
</ul>
// ...
</div>
)
}

The displayed notes (all versus important) are controlled with a button. The event handler
for the button is so simple that it has been defined directly in the attribute of the button
element. The event handler switches the value of showAll from true to false and vice
versa:

() => setShowAll(!showAll) copy

The text of the button depends on the value of the showAll state:

show {showAll ? 'important' : 'all'} copy

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 11 of 18
:
You can find the code for our current application in its entirety in the part2-3 branch of this
GitHub repository .

Exercises 2.6.-2.10.

In the first exercise, we will start working on an application that will be further developed in
the later exercises. In related sets of exercises, it is sufficient to return the final version of
your application. You may also make a separate commit after you have finished each part of
the exercise set, but doing so is not required.

2.6: The Phonebook Step 1

Let's create a simple phonebook. In this part, we will only be adding names to the
phonebook.

Let us start by implementing the addition of a person to the phonebook.

You can use the code below as a starting point for the App component of your application:

import { useState } from 'react' copy

const App = () => {


const [persons, setPersons] = useState([
{ name: 'Arto Hellas' }
])
const [newName, setNewName] = useState('')

return (
<div>
<h2>Phonebook</h2>
<form>
<div>
name: <input />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
...
</div>
)
}

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 12 of 18
:
export default App

The newName state is meant for controlling the form input element.

Sometimes it can be useful to render state and other variables as text for debugging
purposes. You can temporarily add the following element to the rendered component:

<div>debug: {newName}</div> copy

It's also important to put what we learned in the debugging React applications chapter of
part one into good use. The React developer tools extension is incredibly useful for tracking
changes that occur in the application's state.

After finishing this exercise your application should look something like this:

Note the use of the React developer tools extension in the picture above!

NB:

you can use the person's name as a value of the key property

remember to prevent the default action of submitting HTML forms!

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 13 of 18
:
2.7: The Phonebook Step 2

Prevent the user from being able to add names that already exist in the phonebook.
JavaScript arrays have numerous suitable methods for accomplishing this task. Keep in
mind how object equality works in Javascript.

Issue a warning with the alert command when such an action is attempted:

Hint: when you are forming strings that contain values from variables, it is recommended to
use a template string :

`${newName} is already added to phonebook` copy

If the newName variable holds the value Arto Hellas, the template string expression returns
the string

`Arto Hellas is already added to phonebook` copy

The same could be done in a more Java-like fashion by using the plus operator:

newName + ' is already added to phonebook' copy

Using template strings is the more idiomatic option and the sign of a true JavaScript
professional.

2.8: The Phonebook Step 3

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 14 of 18
:
Expand your application by allowing users to add phone numbers to the phone book. You
will need to add a second input element to the form (along with its own event handler):

<form> copy
<div>name: <input /></div>
<div>number: <input /></div>
<div><button type="submit">add</button></div>
</form>

At this point, the application could look something like this. The image also displays the
application's state with the help of React developer tools :

2.9*: The Phonebook Step 4

Implement a search field that can be used to filter the list of people by name:

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 15 of 18
:
You can implement the search field as an input element that is placed outside the HTML
form. The filtering logic shown in the image is case insensitive, meaning that the search term
arto also returns results that contain Arto with an uppercase A.

NB: When you are working on new functionality, it's often useful to "hardcode" some dummy
data into your application, e.g.

const App = () => { copy


const [persons, setPersons] = useState([
{ name: 'Arto Hellas', number: '040-123456', id: 1 },
{ name: 'Ada Lovelace', number: '39-44-5323523', id: 2 },
{ name: 'Dan Abramov', number: '12-43-234345', id: 3 },
{ name: 'Mary Poppendieck', number: '39-23-6423122', id: 4 }
])

// ...
}

This saves you from having to manually input data into your application for testing out your
new functionality.

2.10: The Phonebook Step 5

If you have implemented your application in a single component, refactor it by extracting


suitable parts into new components. Maintain the application's state and all event handlers
in the App root component.

It is sufficient to extract three components from the application. Good candidates for
separate components are, for example, the search filter, the form for adding new people to

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 16 of 18
:
the phonebook, a component that renders all people from the phonebook, and a component
that renders a single person's details.

The application's root component could look similar to this after the refactoring. The
refactored root component below only renders titles and lets the extracted components take
care of the rest.

const App = () => { copy


// ...

return (
<div>
<h2>Phonebook</h2>

<Filter ... />

<h3>Add a new</h3>

<PersonForm
...
/>

<h3>Numbers</h3>

<Persons ... />


</div>
)
}

NB: You might run into problems in this exercise if you define your components "in the
wrong place". Now would be a good time to rehearse the chapter do not define a component
in another component from the last part.

Propose changes to material

< Part 2a Part 2c >


Previous part Next part

About course

Course contents

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 17 of 18
:
Course contents

FAQ

Partners

Challenge

https://fullstackopen.com/en/part2/forms 21/06/2025, 13 04
Page 18 of 18
:

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy