Chapter 12 of 12
Build a complete notes application — full CRUD (Create, Read, Update, Delete), search, and localStorage persistence. This brings together everything from the course: components, props, state, effects, context, and custom hooks.
App.jsx — root, provides NotesContextuseNotes.js — custom hook (CRUD operations + localStorage)NotesList.jsx — renders all notes with search filterNoteCard.jsx — individual note with edit/deleteNoteEditor.jsx — form for creating and editing notesSearchBar.jsx — search inputimport { useState } from "react";
import { useLocalStorage } from "./useLocalStorage";
export function useNotes() {
const [notes, setNotes] = useLocalStorage("notes", []);
const [search, setSearch] = useState("");
const filtered = notes.filter(note =>
note.title.toLowerCase().includes(search.toLowerCase()) ||
note.body.toLowerCase().includes(search.toLowerCase())
);
function addNote({ title, body }) {
setNotes(prev => [
{ id: Date.now(), title, body, createdAt: new Date().toISOString() },
...prev,
]);
}
function updateNote(id, changes) {
setNotes(prev =>
prev.map(note => note.id === id ? { ...note, ...changes } : note)
);
}
function deleteNote(id) {
setNotes(prev => prev.filter(note => note.id !== id));
}
return { notes: filtered, search, setSearch, addNote, updateNote, deleteNote };
}import { createContext, useContext } from "react";
import { useNotes } from "./useNotes";
import NotesList from "./NotesList";
import NoteEditor from "./NoteEditor";
import SearchBar from "./SearchBar";
export const NotesContext = createContext(null);
export default function App() {
const notes = useNotes();
return (
<NotesContext.Provider value={notes}>
<div className="app">
<header>
<h1>Notes</h1>
<SearchBar />
</header>
<main>
<NoteEditor />
<NotesList />
</main>
</div>
</NotesContext.Provider>
);
}