Skip to content

Commit e4ceb28

Browse files
committed
Add the feature to delete a contact and synchronize the url and form state
1 parent 135d2dc commit e4ceb28

File tree

4 files changed

+94
-82
lines changed

4 files changed

+94
-82
lines changed

src/main.jsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import ReactDOM from 'react-dom/client';
33
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
44
// import Root from './routes/root';
5-
import Index from './routes'
5+
import Index from './routes';
66
import ErrorPage from './error-page';
77
import Contact from './routes/contact';
88
import Root, {
@@ -12,9 +12,9 @@ import Root, {
1212
import { loader as contactLoader } from './routes/contact';
1313
import './index.css';
1414
import EditContact from './routes/edit';
15-
import {action as editAction} from './routes/edit'
15+
import { action as editAction } from './routes/edit';
1616
// import DeleteContact from './routes/destroy'
17-
import {action as deleteAction} from './routes/destroy'
17+
import { action as deleteAction } from './routes/destroy';
1818

1919
const router = createBrowserRouter([
2020
{
@@ -24,7 +24,7 @@ const router = createBrowserRouter([
2424
loader: rootLoader,
2525
action: rootAction,
2626
children: [
27-
{index: true, element: <Index />},
27+
{ index: true, element: <Index /> },
2828
{
2929
path: 'contacts/:contactId',
3030
element: <Contact />,
@@ -34,14 +34,14 @@ const router = createBrowserRouter([
3434
path: 'contacts/:contactId/edit',
3535
element: <EditContact />,
3636
loader: contactLoader,
37-
action: editAction
37+
action: editAction,
38+
},
39+
{
40+
path: 'contacts/:contactId/destroy',
41+
// element: <DeleteContact />,
42+
action: deleteAction,
43+
errorElement: <div>Oops! There was an error.</div>,
3844
},
39-
{
40-
path: 'contacts/:contactId/destroy',
41-
// element: <DeleteContact />,
42-
action: deleteAction,
43-
errorElement: <div>Oops! There was an error.</div>,
44-
}
4545
],
4646
},
4747
]);

src/routes/destroy.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { redirect } from 'react-router-dom'
22
import { deleteContact } from '../contacts'
33

44
export async function action ({params}) {
5-
throw new Error('Ohh dang')
5+
// throw new Error('Ohh dang')
66
await deleteContact(params.contactId)
77
return redirect("/")
88
}

src/routes/edit.jsx

Lines changed: 67 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,72 @@
1-
import { Form, useLoaderData, redirect } from "react-router-dom";
2-
import { updateContact } from "../contacts";
1+
import { Form, useLoaderData, redirect, useNavigate } from 'react-router-dom';
2+
import { updateContact } from '../contacts';
33

4-
export async function action({request, params}) {
5-
const formData = await request.formData();
6-
const first = formData.get('first')
7-
const last = formData.get('last')
8-
const updates = Object.fromEntries(formData)
9-
console.log(updates, request, params, '\n', first, last);
10-
await updateContact(params.contactId, updates)
11-
return redirect(`/contacts/${params.contactId}`)
4+
export async function action({ request, params }) {
5+
const formData = await request.formData();
6+
const first = formData.get('first');
7+
const last = formData.get('last');
8+
const updates = Object.fromEntries(formData);
9+
console.log(updates, request, params, '\n', first, last);
10+
await updateContact(params.contactId, updates);
11+
return redirect(`/contacts/${params.contactId}`);
1212
}
1313

1414
export default function EditContact() {
15-
const { contact } = useLoaderData();
16-
17-
return (
18-
<Form method="post" id="contact-form">
19-
<p>
20-
<span>Name</span>
21-
<input
22-
placeholder="First"
23-
aria-label="First name"
24-
type="text"
25-
name="first"
26-
defaultValue={contact.first}
27-
/>
28-
<input
29-
placeholder="Last"
30-
aria-label="Last name"
31-
type="text"
32-
name="last"
33-
defaultValue={contact.last}
34-
/>
35-
</p>
36-
<label>
37-
<span>Twitter</span>
38-
<input
39-
type="text"
40-
name="twitter"
41-
placeholder="@jack"
42-
defaultValue={contact.twitter}
43-
/>
44-
</label>
45-
<label>
46-
<span>Avatar URL</span>
47-
<input
48-
placeholder="https://example.com/avatar.jpg"
49-
aria-label="Avatar URL"
50-
type="text"
51-
name="avatar"
52-
defaultValue={contact.avatar}
53-
/>
54-
</label>
55-
<label>
56-
<span>Notes</span>
57-
<textarea
58-
name="notes"
59-
defaultValue={contact.notes}
60-
rows={6}
61-
/>
62-
</label>
63-
<p>
64-
<button type="submit">Save</button>
65-
<button type="button">Cancel</button>
66-
</p>
67-
</Form>
68-
);
15+
const { contact } = useLoaderData();
16+
const navigate = useNavigate();
17+
return (
18+
<Form method='post' id='contact-form'>
19+
<p>
20+
<span>Name</span>
21+
<input
22+
placeholder='First'
23+
aria-label='First name'
24+
type='text'
25+
name='first'
26+
defaultValue={contact.first}
27+
/>
28+
<input
29+
placeholder='Last'
30+
aria-label='Last name'
31+
type='text'
32+
name='last'
33+
defaultValue={contact.last}
34+
/>
35+
</p>
36+
<label>
37+
<span>Twitter</span>
38+
<input
39+
type='text'
40+
name='twitter'
41+
placeholder='@jack'
42+
defaultValue={contact.twitter}
43+
/>
44+
</label>
45+
<label>
46+
<span>Avatar URL</span>
47+
<input
48+
placeholder='https://example.com/avatar.jpg'
49+
aria-label='Avatar URL'
50+
type='text'
51+
name='avatar'
52+
defaultValue={contact.avatar}
53+
/>
54+
</label>
55+
<label>
56+
<span>Notes</span>
57+
<textarea name='notes' defaultValue={contact.notes} rows={6} />
58+
</label>
59+
<p>
60+
<button type='submit'>Save</button>
61+
<button
62+
type='button'
63+
onClick={() => {
64+
navigate(-1);
65+
}}
66+
>
67+
Cancel
68+
</button>
69+
</p>
70+
</Form>
71+
);
6972
}

src/routes/root.jsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useEffect } from 'react';
12
import {
23
Outlet,
34
NavLink,
@@ -7,9 +8,11 @@ import {
78
} from 'react-router-dom';
89
import { getContacts, createContact } from '../contacts';
910

10-
export const loader = async () => {
11-
const contacts = await getContacts();
12-
return { contacts };
11+
export const loader = async ({ request }) => {
12+
const url = new URL(request.url);
13+
const q = url.searchParams.get('q');
14+
const contacts = await getContacts(q);
15+
return { contacts, q };
1316
};
1417

1518
export const action = async () => {
@@ -18,24 +21,30 @@ export const action = async () => {
1821
};
1922

2023
export default function Root() {
21-
const { contacts } = useLoaderData();
24+
const { contacts, q } = useLoaderData();
2225
const navigation = useNavigation();
26+
27+
// Synchronize the URL and Form state
28+
useEffect(() => {
29+
document.getElementById("q").value = q
30+
}, [q])
2331
return (
2432
<>
2533
<div id='sidebar'>
2634
<h1>React Router Contacts</h1>
2735
<div>
28-
<form id='search-form' role='search'>
36+
<Form id='search-form' role='search'>
2937
<input
3038
id='q'
3139
aria-label='Search contacts'
3240
placeholder='Search'
3341
type='search'
3442
name='q'
43+
defaultValue={q}
3544
/>
3645
<div id='search-spinner' aria-hidden hidden={true} />
3746
<div className='sr-only' aria-live='polite'></div>
38-
</form>
47+
</Form>
3948
<Form method='post'>
4049
<button type='submit'>New</button>
4150
</Form>

0 commit comments

Comments
 (0)