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

part2d

Altering data in server

Sending data to the server

App.js
const addNote = (event) => {
event.preventDefault();
const noteObject = {
content: newNote,
data: new Date().toISOString(),
important: Math.random() < 0.5,
id: String(notes.length + 1),
};
axios.post("http://localhost:3001/notes", noteObject).then((response) => {
setNotes(notes.concat(noteObject));
setNewNote("");
});
};

Changing the importance of notes

const App = () => {
const [notes, setNotes] = useState([])
const [newNote, setNewNote] = useState('')
const [showAll, setShowAll] = useState(true)
// ...
const toggleImportanceOf = id => {
const url = `http://localhost:3001/notes/${id}`
const note = notes.find(n => n.id === id)
const changedNote = { ...note, important: !note.important }
axios.put(url, changedNote).then(response => {
setNotes(notes.map(n => n.id !== id ? n : response.data))
})
}
// ...
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}
toggleImportance={() => toggleImportanceOf(note.id)}
/>
)}
</ul>
// ...
</div>
)
}

Extracting Communication with the Backend into a Separate Module

Add folder src/services and add new file notes.js

src/services/notes.js
import axios from "axios";
const baseUrl = "http://localhost:3001/notes";
const getAll = () => {
const request = axios.get(baseUrl);
return request.then((response) => response.data);
};
const create = (newObject) => {
const request = axios.post(baseUrl, newObject);
return request.then((response) => response.data);
};
const update = (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject);
return request.then((response) => response.data);
};
export default { getAll, create, update };

import in App.js and use the noteService

App.js
import { useState, useEffect } from "react";
import Note from "./components/Note";
import noteService from "./services/notes";
const App = () => {
const [notes, setNotes] = useState([]);
const [newNote, setNewNote] = useState("");
const [showAll, setShowAll] = useState(true);
useEffect(() => {
noteService.getAll().then((initialNotes) => {
setNotes(initialNotes);
});
}, []);
console.log("render", notes.length, "notes");
const addNote = (event) => {
event.preventDefault();
const noteObject = {
content: newNote,
data: new Date().toISOString(),
important: Math.random() < 0.5,
id: String(notes.length + 1),
};
noteService.create(noteObject).then((returnedNote) => {
setNotes(notes.concat(returnedNote));
setNewNote("");
});
};
const handleNoteChange = (event) => {
console.log(event.target.value);
setNewNote(event.target.value);
};
const notesToShow = showAll ? notes : notes.filter((note) => note.important);
const toggleImportanceOf = (id) => {
const note = notes.find((n) => n.id === id);
const changedNote = { ...note, important: !note.important };
noteService.update(id, changedNote).then((returnedNote) => {
setNotes(notes.map((note) => (note.id !== id ? note : returnedNote)));
});
};
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}
toggleImportance={() => toggleImportanceOf(note.id)}
/>
))}
</ul>
<form onSubmit={addNote}>
<input value={newNote} onChange={handleNoteChange} />
<button type="submit">save</button>
</form>
</div>
);
};
export default App;

Below two lines have been actioned.

return response data directly

cleaner syntax for defining object literals

Promises and Errors

App.js
// ...
const toggleImportanceOf = (id) => {
const note = notes.find((n) => n.id === id);
const changedNote = { ...note, important: !note.important };
noteService
.update(id, changedNote)
.then((returnedNote) => {
setNotes(notes.map((note) => (note.id !== id ? note : returnedNote)));
})
.catch((error) => {
alert(`the note '${note.content}' was already deleted from server`);
setNotes(notes.filter((n) => n.id !== id));
});
};
// ...

Full stack developer’s oath

Full stack development is extremely hard, that is why I will use all the possible means to make it easier

  • I will have my browser developer console open all the time
  • I will use the network tab of the browser dev tools to ensure that frontend and backend are communicating as I expect

  • I will constantly keep an eye on the state of the server to make sure that the data sent there by the frontend is saved there as I expect

  • I will progress with small steps
  • I will write lots of console.log statements to make sure I understand how the code behaves and to help pinpoint problems
  • If my code does not work, I will not write more code. Instead, I start deleting the code until it works or just return to a state when everything was still working
  • When I ask for help in the course Discord or Telegram channel or elsewhere I formulate my questions properly, see here how to ask for help

Exercises

2.12 The phonebook step 7

Add number saves to the backend server.

2.13 The phonebook step 8

Extract the code that handles the communication with the backend into its own module.

2.14 The phonebook step 9

Delete button with a window.confirm method confirming.

2.15* The phonebook step 10

Change the functionality so that if a number is added to an already existing user, the new number will replace the old number. It’s recommended to use the HTTP PUT method for updating the phone number.

If the person’s information is already in the phonebook, the application can ask the user to confirm the action.

My solutions

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

2.12 The phonebook step 7

Add number saves to the backend server.

2.13 The phonebook step 8

Extract the code that handles the communication with the backend into its own module.

import axios from 'axios'
const baseUrl = 'http://localhost:3001/persons'
const getAll = () => {
const request = axios.get(baseUrl)
return request.then(response => response.data)
}
const create = newObject => {
const request = axios.post(baseUrl, newObject)
return request.then(response => response.data)
}
const update = (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject)
return request.then(response => response.data)
}
export default { getAll, create, update }

2.14 The phonebook step 9

Delete button with a window.confirm method confirming.

import React from 'react'
const Person = ({ person, handleClick }) => {
return (
<p key={person.name}>
{person.name} {person.number}
<button onClick={() => handleClick(person)}>delete</button>
</p>
)
}
const Persons = (props) => {
const { personsToShow, handleClick } = props
return (
<>
{
personsToShow.map(
person => <Person key={person.id} person={person} handleClick={handleClick} />
)
}
</>
)
}
export default Persons

2.15 The phonebook step 10

When adding person,
if exist, window.confirm, if confirmed, update the phone number
else add the person.

addPerson in App.js
const addPerson = (event) => {
event.preventDefault();
const personObject = {
name: newName,
number: newNumber,
id: String(persons.length + 1),
};
const existPersonArray = persons.filter((person) =>
_.includes(person, newName)
);
// console.log(existPersonArray)
if (existPersonArray.length > 0) {
// alert(`${newName} is already added to phonebook`)
const existPerson = existPersonArray[0];
// console.log(existPerson)
if (
window.confirm(
`${newName} is already added to phonebook, replace the old number with a new one?`
)
) {
const id = existPerson.id;
personService
.update(id, personObject)
.then((returnedPerson) => {
setPersons(
persons.map((person) =>
person.id !== id ? person : returnedPerson
)
);
setNewName("");
setNewNumber("");
})
.catch((error) => {
alert(
`the person '${existPerson.name}' was already deleted from server`
);
setPersons(persons.filter((person) => person.id !== id));
});
}
} else {
personService.create(personObject).then((returnedPerson) => {
setPersons(persons.concat(returnedPerson));
setNewName("");
setNewNumber("");
});
}
};