Terakhir diperbaharui: Feb 14, 2021
Membuat Front End #3
Daftar Isi
Menyimpan Data Navigasi antar page Menyimpan Notes Menampilkan Notes Edit Notes Hapus Notes Hasil AkhirPada bagian ini kita akan menyempurnakan Front End dari aplikasi DinoTes dengan menambahkan:
- Data
- Navigasi antar page
- Event Handler
Menyimpan Data
Ketika kita membuat sebuah note baru maka note tersebut harus disimpan di suatu tempat, ada beberapa pilihan tempat menyimpan data:
- Component State, tapi data akan hilang ketika browser di-refresh
- Database, ini adalah tempat paling ideal untuk menyimpan data sebuah aplikasi, tapi karena kita hanya fokus pada Front End sedangkan Database umumnya 'hidup' di server maka kita tidak akan menggunakan Database untuk saat ini
- Local Data Storage / Database, seperti sqlite, realm adalah media penyimpanan yang bisa digunakan di sisi client, tapi kita perlu belajar konsep yang digunakan terlebih dahulu
- Browser localStorage, media penyimpanan yang ada pada browser ini dapat kita gunakan untuk menyimpan data sementara, data tidak akan hilang selama data tidak dihapus(clear browsing data/cache)
Kita ambil yang paling simple, yaitu localStorage.
Cara ini tidak direkomendasikan dalam membuat aplikasi, ini hanya untuk sementara dan kita akan menggantinya dengan Database ketika membuat bagian Back End dari Dinotes.
Navigasi antar page
Sampai saat ini untuk mengakses Add page kita harus mengaksesnya dengan alamat http://localhost:3000/add, seharusnya hal ini bisa dilakukan dengan klik button Add New Note.
Kita perbaiki navigasi antar page, termasuk menambahkan breadcumb untuk navigasi kembali ke Home.
Step by step
1.Edit Home Page
src/pages/Home.js
1import React from 'react';2import { Link } from 'react-router-dom';3import PageLayout from '../layouts/PageLayout';4import NotesList from '../components/NotesList';5import Container from '../components/ui/Container';6import Button from '../components/ui/Button';78const HomePage = () => {9 return (10 <PageLayout>11 <Container>12 <Link to="/add">13 <Button>Add New Note</Button>14 </Link>15 <h1>All Notes</h1>16 <NotesList>Notes List</NotesList>17 </Container>18 </PageLayout>19 );20};2122export default HomePage;
- Menambah breadcumb untuk navigasi kembali ke page Home.
src/pages/Add.js
1import React from 'react';2import { Link } from 'react-router-dom';3import PageLayout from '../layouts/PageLayout';4import AddNoteForm from '../components/AddNoteForm';5import Container from '../components/ui/Container';67const AddPage = () => {8 return (9 <PageLayout>10 <Container>11 <div>12 <h4>13 <Link to="/">Home</Link> / Add14 </h4>15 </div>16 <h2>Add New Note</h2>17 <AddNoteForm />18 </Container>19 </PageLayout>20 );21};2223export default AddPage;
src/pages/Edit.js
1import React from 'react';2import { Link } from 'react-router-dom';3import PageLayout from '../layouts/PageLayout';4import EditNoteForm from '../components/EditNoteForm';5import Container from '../components/ui/Container';67const EditPage = () => {8 return (9 <PageLayout>10 <Container>11 <div>12 <h4>13 <Link to="/">Home</Link> / Edit{' '}14 </h4>15 </div>16 <h1>Edit Note</h1>17 <EditNoteForm />18 </Container>19 </PageLayout>20 );21};2223export default EditPage;
Menyimpan notes
- Edit src/components/AddNoteForm.js.
1import React, { useState } from 'react';2import { Form, FormGroup, Label, Input, TextArea } from '../components/ui/Form';3import { v4 as uuidv4 } from 'uuid';4import Button from '../components/ui/Button';56const AddNoteForm = () => {7 const [state, setState] = useState({ title: '', note: '' });89 const handleTitleChange = (e) => {10 setState({ ...state, title: e.target.value });11 };1213 const handleNoteChange = (e) => {14 setState({ ...state, note: e.target.value });15 };1617 const handleSubmit = (e) => {18 let existing = localStorage.getItem('notes');1920 existing = existing ? JSON.parse(existing) : [];2122 const noteId = uuidv4();2324 existing[noteId] = state;2526 localStorage.setItem('notes', JSON.stringify(existing));2728 e.preventDefault();29 };3031 const { title, note } = state;3233 return (34 <Form onSubmit={handleSubmit}>35 <FormGroup>36 <Label>Title</Label>37 <Input type="text" name="title" value={title} onChange={handleTitleChange} />38 </FormGroup>39 <FormGroup>40 <Label>Note</Label>41 <TextArea name="note" rows="12" value={note} onChange={handleNoteChange} />42 </FormGroup>43 <FormGroup>44 <Button type="submit">Add</Button>45 </FormGroup>46 </Form>47 );48};4950export default AddNoteForm;
OK, mari kita jelaskan code di atas satu-persatu.
- Kita buat state untuk menyimpan data note, data note sendiri berupa object dengan properties title dan note
1const [state, setState] = useState({ title: '', note: '' });
- handleTitleChange() dan handleNoteChange() adalah event handler untuk menyimpan data dari form input ke dalam state
1const handleTitleChange = (e) => {2 setState({ ...state, title: e.target.value });3};45const handleNoteChange = (e) => {6 setState({ ...state, note: e.target.value });7};
- Penjelasan handleSubmit():
1const handleSubmit = (e) => {2 /*3 Ambil data 'notes' di dalam localStorage4 Simpan ke dalam variable bernama existing5 */6 let existing = localStorage.getItem('notes');78 /*9 Cek jika sudah ada data bernama 'notes' di dalam localStorage10 Jika ada maka gunakan method JSON.parse() untuk membaca datanya11 Hal ini dikarenakan localStorage hanya bisa menyimpan tipe data String12 Jika tidak ada data di dalam 'notes' maka buat sebuah array kosong []13 */14 existing = existing ? JSON.parse(existing) : [];1516 /*17 Setiap note harus memiliki id unik untuk membedakan dengan data note yang lain18 Kita bisa menggunakan package uuid dari npm19 Untuk menginstallnya gunakan command => yarn add uuid20 */21 const noteId = uuidv4();2223 /*24 Tambahkan data note + noteId di dalam state ke array existing25 */26 existing.push({ ...state, id: noteId });2728 /*29 Simpan data ke localStorage dengan command localStorage.setItem()30 Gunakan method JSON.stringify() untuk mengubah object ke String31 Karena localStorage hanya bisa menyimpan tipe data String32 */33 localStorage.setItem('notes', JSON.stringify(existing));3435 // membiarkan react menghandle data yang disubmit36 e.preventDefault();37};
Menampilkan notes
Edit src/components/NotesList.js.
1import React from 'react';2import styled from 'styled-components';34const NotesListContainer = styled.div`5678910111213`;1415const List = styled.ul`1617`;1819const ListItem = styled.li`2021`;2223const NotesList = () => {24 const existing = localStorage.getItem('notes');2526 const notes = existing ? JSON.parse(existing) : [];2728 const listItems = notes.map((note) => {29 return (30 <ListItem key={note.id}>31 <h4>{note.title}</h4>32 </ListItem>33 );34 });3536 return (37 <NotesListContainer>38 <List>{listItems}</List>39 </NotesListContainer>40 );41};4243export default NotesList;
Pada code di atas semua notes ditampilkan dalam bentuk list.
1const listItems = notes.map((note) => {2 return (3 <ListItem key={note.id}>4 <h4>{note.title}</h4>5 </ListItem>6 );7});
Edit Notes
Metode yang digunakan untuk edit adalah click-to-edit sehingga user bisa edit note dengan click title dari note tersebut.
Jika title dari note di klik maka akan mengarah ke alamat http://localhost/edit/{noteId}.
Step by step
- Edit src/components/NotesList.js
1import React from 'react';2import styled from 'styled-components';3import { Link } from 'react-router-dom';45const NotesListContainer = styled.div`67891011121314`;1516const List = styled.ul`1718`;1920const ListItem = styled.li`2122`;2324const NotesList = () => {25 const existing = localStorage.getItem('notes');2627 const notes = existing ? JSON.parse(existing) : [];2829 const listItems = notes.map((note) => {30 return (31 <ListItem key={note.id}>32 <h4>33 <Link to={`/edit/${note.id}`}>{note.title}</Link>34 </h4>35 </ListItem>36 );37 });3839 return (40 <NotesListContainer>41 <List>{listItems}</List>42 </NotesListContainer>43 );44};4546export default NotesList;
- Edit src/App.js agar Route component yang mengarahkan ke page Edit bisa menerima URL parameter.
1import React from 'react';2import { Route, Switch } from 'react-router-dom';3import styled from 'styled-components';4import HomePage from './pages/Home';5import AddPage from './pages/Add';6import EditPage from './pages/Edit';78const Container = styled.div`910`;1112function App() {13 return (14 <Container>15 <Switch>16 <Route path="/add">17 <AddPage />18 </Route>19 <Route path="/edit/:id">20 <EditPage />21 </Route>22 <Route path="/">23 <HomePage />24 </Route>25 </Switch>26 </Container>27 );28}2930export default App;
- Edit src/components/Edit.js untuk menampilkan data dari localStorage sesuai note id.
1import React, { useEffect, useState } from 'react';2import { useLocation } from 'react-router-dom';3import { Form, FormGroup, Label, Input, TextArea } from './ui/Form';4import Button from './ui/Button';56const EditNoteForm = () => {7 const location = useLocation();8 const [state, setState] = useState({ title: '', note: '' });910 useEffect(() => {11 const existing = localStorage.getItem('notes');1213 const notes = existing ? JSON.parse(existing) : [];1415 const noteId = location.pathname.replace('/edit/', '');1617 const currentNote = notes.filter((note) => note.id === noteId);1819 setState(currentNote[0]);20 }, []);2122 const { title, note } = state;2324 return (25 <Form>26 <FormGroup>27 <Label>Title</Label>28 <Input type="text" name="title" value={title} />29 </FormGroup>30 <FormGroup>31 <Label>Note</Label>32 <TextArea name="note" rows="12" value={note} />33 </FormGroup>34 <FormGroup>35 <Button type="submit">Save</Button>36 <Button>Delete</Button>37 </FormGroup>38 </Form>39 );40};4142export default EditNoteForm;
Penjelasan: (sebagian code).
1/*2Custom Hooks dari package react-router-dom3Untuk mendapatkan alamat dari page yang sedang diakses4*/5const location = useLocation();67...89/*10Proses pengambilan data dilakukan setelah DOM diupdate11Oleh karena itu kita menggunakan useEffect()12*/13useEffect(() => {1415 ...1617 // mengambil noteId dari location18 const noteId = location.pathname.replace('/edit/', '');1920 // mengambil data note yang akan diedit21 const currentNote = notes.filter((note) => note.id === noteId);2223 // simpan data note ke state24 setState(currentNote[0]);2526...
- Tambahkan event handler agar data yang sudah diedit bisa disimpan kembali ke dalam localStorage.
src/components/Edit.js
1import React, { useEffect, useState } from 'react';2import { useLocation } from 'react-router-dom';3import { Form, FormGroup, Label, Input, TextArea } from './ui/Form';4import Button from './ui/Button';56const EditNoteForm = () => {7 const location = useLocation();8 const [allNotes, setAllNotes] = useState(null);9 const [currentNote, setCurrentNote] = useState({ title: '', note: '' });1011 useEffect(() => {12 const existing = localStorage.getItem('notes');1314 const notes = existing ? JSON.parse(existing) : [];1516 setAllNotes(notes);1718 const noteId = location.pathname.replace('/edit/', '');1920 const currentNote = notes.filter((note) => note.id === noteId);2122 setCurrentNote(currentNote[0]);23 }, []);2425 const handleTitleChange = (e) => {26 setCurrentNote({ ...currentNote, title: e.target.value });27 };2829 const handleNoteChange = (e) => {30 setCurrentNote({ ...currentNote, note: e.target.value });31 };3233 const handleSubmit = (e) => {34 const newNotes = allNotes.map((note) => {35 if (note.id === currentNote.id) {36 return { ...note, title: currentNote.title, note: currentNote.note };37 } else {38 return note;39 }40 });4142 localStorage.setItem('notes', JSON.stringify(newNotes));4344 e.preventDefault();45 };4647 const { title, note } = currentNote;4849 return (50 <Form onSubmit={handleSubmit}>51 <FormGroup>52 <Label>Title</Label>53 <Input type="text" name="title" value={title} onChange={handleTitleChange} />54 </FormGroup>55 <FormGroup>56 <Label>Note</Label>57 <TextArea name="note" rows="12" value={note} onChange={handleNoteChange} />58 </FormGroup>59 <FormGroup>60 <Button type="submit">Save</Button>61 <Button>Delete</Button>62 </FormGroup>63 </Form>64 );65};6667export default EditNoteForm;
Penjelasan:
- Rename State
Untuk update data notes di localStorage yang dilakukan adalah mengambil semua data localStorage kemudian data disimpan ke dalam state bernama allNotes untuk selanjutnya menggantikan data yang lama.
Oleh karena itu kita rename state dan menambah state yang baru untuk menyimpan data semua notes.
1...2 const [allNotes, setAllNotes] = useState(null);3 const [currentNote, setCurrentNote] = useState({ title: '', note: '' });4...
- handleSubmit()
1...2 const handleSubmit = (e) => {3 // update data notes4 const newNotes = allNotes.map((note) => {5 if (note.id === currentNote.id) {6 return { ...note, title: currentNote.title, note: currentNote.note };7 } else {8 return note;9 }10 });1112 // replace data notes yang lama di local storage13 localStorage.setItem('notes', JSON.stringify(newNotes));1415 e.preventDefault();16 };17...
Hapus Notes
Tambahkan event handler untuk menghapus note ketika user klik button Delete.
1import React, { useEffect, useState } from 'react';2import { useLocation, useHistory } from 'react-router-dom';3import { Form, FormGroup, Label, Input, TextArea } from './ui/Form';4import Button from './ui/Button';56const EditNoteForm = () => {7 const location = useLocation();8 const history = useHistory();9 const [allNotes, setAllNotes] = useState(null);10 const [currentNote, setCurrentNote] = useState({ title: '', note: '' });1112 useEffect(() => {13 const existing = localStorage.getItem('notes');1415 const notes = existing ? JSON.parse(existing) : [];1617 setAllNotes(notes);1819 const noteId = location.pathname.replace('/edit/', '');2021 const currentNote = notes.filter((note) => note.id === noteId);2223 setCurrentNote(currentNote[0]);24 }, []);2526 const handleTitleChange = (e) => {27 setCurrentNote({ ...currentNote, title: e.target.value });28 };2930 const handleNoteChange = (e) => {31 setCurrentNote({ ...currentNote, note: e.target.value });32 };3334 const handleSubmit = (e) => {35 const newNotes = allNotes.map((note) => {36 if (note.id === currentNote.id) {37 return { ...note, title: currentNote.title, note: currentNote.note };38 } else {39 return note;40 }41 });4243 localStorage.setItem('notes', JSON.stringify(newNotes));4445 e.preventDefault();46 };4748 const handleDeleteNote = (e) => {49 const newNotes = allNotes.filter((note) => note.id !== currentNote.id);5051 setCurrentNote(null);5253 setAllNotes(newNotes);5455 localStorage.setItem('notes', JSON.stringify(newNotes));5657 history.push('/');58 };5960 const { title, note } = currentNote;6162 return (63 <Form onSubmit={handleSubmit}>64 <FormGroup>65 <Label>Title</Label>66 <Input type="text" name="title" value={title} onChange={handleTitleChange} />67 </FormGroup>68 <FormGroup>69 <Label>Note</Label>70 <TextArea name="note" rows="12" value={note} onChange={handleNoteChange} />71 </FormGroup>72 <FormGroup>73 <Button type="submit">Save</Button>74 <Button onClick={handleDeleteNote}>Delete</Button>75 </FormGroup>76 </Form>77 );78};7980export default EditNoteForm;
Penjelasan: (sebagian code).
1...23/*4Custom Hooks dari package react-router-dom5Dapat digunakan untuk navigasi ke page atau component yang lain6*/7 const history = useHistory();89 ...1011 const handleDeleteNote = (e) => {12 /*13 Daripada menghapus note dari data notes lama14 Kita membuat array baru berisi notes tanpa memasukan note yang dihapus15 */16 const newNotes = allNotes.filter((note) => note.id !== currentNote.id);1718 // kosongkan state setelah dihapus19 setCurrentNote(null);2021 // simpan data notes baru ke state allNotes22 setAllNotes(newNotes);2324 // update data di localStorage25 localStorage.setItem('notes', JSON.stringify(newNotes));2627 // navigasi ke page Home setelah note dihapus28 history.push('/');29 };