Built at: 2024-10-16T09:55:25.150Z Skip to content

part2b

Forms

add notes state

App.js
import { useState } from "react";
import Note from "./components/Note";
const App = (props) => {
const [notes, setNotes] = useState(props.notes);
return (
<div>
<h1>Notes</h1>
<ul>
{notes.map((note) => (
<Note key={note.id} note={note} />
))}
</ul>
</div>
);
};
export default App;

add form and event handler

App.js
const App = (props) => {
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>
);
};

Controlled component (input)

Deal with form input, notice about the onChange warning.

App.js
const App = (props) => {
const [notes, setNotes] = useState(props.notes);
const [newNote, setNewNote] = useState("a new note...");
const addNote = (event) => {
event.preventDefault();
console.log("button clicked", event.target);
};
const handleNoteChange = (event) => {
console.log(event.target.value);
setNewNote(event.target.value);
};
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>
);
};

Complete addNote function

App.js
const addNote = (event) => {
event.preventDefault();
const noteObject = {
content: newNote,
date: new Date().toISOString(),
important: Math.random() < 0.5,
id: notes.length + 1,
};
setNotes(notes.concat(noteObject));
setNewNote("");
};

Filtering Displayed Elements

App.js
const App = (props) => {
const [notes, setNotes] = useState(props.notes);
const [newNote, setNewNote] = useState("");
const [showAll, setShowAll] = useState(true);
// ...
};

Change render notes to notesToShow

App.js
import { useState } from "react";
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>
);
};

Add the filter button

App.js
import { useState } from "react";
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>
);
};

Exercises

2.6 The phonebook step 1

A phonebook that can add name and show the names.

Start point

App.js
import { useState } from "react";
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>
);
};
export default App;

2.7 The phonebook step 2

Prevent the use from being able to add names that already exist in the phonebook. Alert when such an action is attempted.

ref: lodash isEqual

2.8 The phonebook step 3

Allow users to add phone numbers.

2.9* The phonebook step 4

Implement a search field to filter results.

dummy data
const App = () => {
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 },
]);
// ...
};

2.10 The phonebook step 5

Extract three components: Filter, PersonForm, Persons(and Person)

ref:

const App = () => {
// ...
return (
<div>
<h2>Phonebook</h2>
<Filter ... />
<h3>Add a new</h3>
<PersonForm
...
/>
<h3>Numbers</h3>
<Persons ... />
</div>
)
}

My solutions

My solutions:
(Please complete your own solutions before click here.)

2.6 Phonebook that can add name

App.js
import { useState } from "react";
const App = () => {
const [persons, setPersons] = useState([{ name: "Arto Hellas" }]);
const [newName, setNewName] = useState("");
const handleNameChange = (event) => {
event.preventDefault();
setNewName(event.target.value);
};
const addPerson = (event) => {
event.preventDefault();
setPersons(
persons.concat({
name: newName,
})
);
setNewName("");
};
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={addPerson}>
<div>
name: <input value={newName} onChange={handleNameChange} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
{persons.map((person) => (
<p key={person.name}>{person.name}</p>
))}
</div>
);
};
export default App;

2.7 no same person name

To use lodash _.isEqual

pnpm i lodash
check when adding person
const addPerson = (event) => {
event.preventDefault();
// console.log(persons)
// console.log({ name: newName })
// console.log(_.includes(persons, { name: newName }))
// console.log(persons.filter(person => _.isEqual(person, { name: newName })).length > 0)
if (
persons.filter((person) => _.isEqual(person, { name: newName })).length > 0
) {
alert(`${newName} is already added to phonebook`);
return;
}
setPersons(
persons.concat({
name: newName,
})
);
setNewName("");
};

2.8 second input form to add number

App.js
import { useState } from "react";
var _ = require("lodash");
const App = () => {
const [persons, setPersons] = useState([
{ name: "Arto Hellas", number: "040-1234567" },
]);
const [newName, setNewName] = useState("");
const [newNumber, setNewNumber] = useState("");
const handleNameChange = (event) => {
event.preventDefault();
setNewName(event.target.value);
};
const handleNumberChange = (event) => {
event.preventDefault();
setNewNumber(event.target.value);
};
const addPerson = (event) => {
event.preventDefault();
// console.log(persons)
// console.log({ name: newName })
// console.log(_.includes(persons, { name: newName }))
// console.log(persons.filter(person => _.isEqual(person, { name: newName })).length > 0)
// console.log(persons)
// console.log(persons.filter(person => _.includes(person, newName)).length > 0)
if (persons.filter((person) => _.includes(person, newName)).length > 0) {
alert(`${newName} is already added to phonebook`);
return;
}
setPersons(
persons.concat({
name: newName,
number: newNumber,
})
);
setNewName("");
setNewNumber("");
};
return (
<div>
<h2>Phonebook</h2>
<form onSubmit={addPerson}>
<div>
name: <input value={newName} onChange={handleNameChange} /> <br />
number: <input value={newNumber} onChange={handleNumberChange} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
{persons.map((person) => (
<p key={person.name}>
{person.name} {person.number}
</p>
))}
</div>
);
};
export default App;

2.9 filter

App.js
import { useState } from "react";
var _ = require("lodash");
const App = () => {
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 },
]);
const [newName, setNewName] = useState("");
const [newNumber, setNewNumber] = useState("");
const [filterName, setFilterName] = useState("");
const handleNameChange = (event) => {
event.preventDefault();
setNewName(event.target.value);
};
const handleNumberChange = (event) => {
event.preventDefault();
setNewNumber(event.target.value);
};
const addPerson = (event) => {
event.preventDefault();
// console.log(persons)
// console.log({ name: newName })
// console.log(_.includes(persons, { name: newName }))
// console.log(persons.filter(person => _.isEqual(person, { name: newName })).length > 0)
// console.log(persons)
// console.log(persons.filter(person => _.includes(person, newName)).length > 0)
if (persons.filter((person) => _.includes(person, newName)).length > 0) {
alert(`${newName} is already added to phonebook`);
return;
}
setPersons(
persons.concat({
name: newName,
number: newNumber,
id: persons.length + 1,
})
);
setNewName("");
setNewNumber("");
};
const handleFilterName = (event) => {
event.preventDefault();
setFilterName(event.target.value);
};
const personsToShow = persons.filter((person) =>
_.includes(person.name.toLowerCase(), filterName.toLowerCase())
);
return (
<div>
<h2>Phonebook</h2>
<form>
<div>
filter shown with:
<input value={filterName} onChange={handleFilterName} />
</div>
</form>
<h2>add a new</h2>
<form onSubmit={addPerson}>
<div>
name: <input value={newName} onChange={handleNameChange} /> <br />
number: <input value={newNumber} onChange={handleNumberChange} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
<h2>Numbers</h2>
{personsToShow.map((person) => (
<p key={person.name}>
{person.name} {person.number}
</p>
))}
</div>
);
};
export default App;

2.10 component modules

import { useState } from 'react'
import Filter from './components/Filter'
import PersonForm from './components/PersonForm'
import Persons from './components/Persons'
var _ = require('lodash')
const App = () => {
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 }
])
const [newName, setNewName] = useState('')
const [newNumber, setNewNumber] = useState('')
const [filterName, setFilterName] = useState('')
const handleNameChange = (event) => {
event.preventDefault()
setNewName(event.target.value)
}
const handleNumberChange = (event) => {
event.preventDefault()
setNewNumber(event.target.value)
}
const addPerson = (event) => {
event.preventDefault()
if (persons.filter(person => _.includes(person, newName)).length > 0) {
alert(`${newName} is already added to phonebook`)
return
}
setPersons(persons.concat({
name: newName,
number: newNumber,
id: persons.length + 1
}))
setNewName('')
setNewNumber('')
}
const handleFilterName = (event) => {
event.preventDefault()
setFilterName(event.target.value)
}
const personsToShow = persons.filter(
person => _.includes(person.name.toLowerCase(), filterName.toLowerCase())
)
return (
<div>
<h2>Phonebook</h2>
<Filter filterName={filterName} onChange={handleFilterName} />
<h2>Add a new</h2>
<PersonForm addPerson={addPerson} newName={newName} newNumber={newNumber} handleNameChange={handleNameChange} handleNumberChange={handleNumberChange} />
<h2>Numbers</h2>
<Persons personsToShow={personsToShow} />
</div>
)
}
export default App