Skip to content

Commit 7ff25fa

Browse files
committed
Add edit router
1 parent 9732532 commit 7ff25fa

File tree

4 files changed

+174
-77
lines changed

4 files changed

+174
-77
lines changed

src/main.jsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,35 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
44
// import Root from './routes/root';
55
import ErrorPage from './error-page';
66
import Contact from './routes/contact';
7-
import Root, {loader as rootLoader} from './routes/root'
8-
// import App from './App.jsx'
7+
import Root, {
8+
loader as rootLoader,
9+
action as rootAction,
10+
} from './routes/root';
11+
import { loader as contactLoader } from './routes/contact';
912
import './index.css';
10-
13+
import EditContact from './routes/edit';
14+
import {action as editAction} from './routes/edit'
1115

1216
const router = createBrowserRouter([
1317
{
1418
path: '/',
1519
element: <Root />,
1620
errorElement: <ErrorPage />,
17-
loader: rootLoader,
18-
children: [{ path: 'contacts/:contactId', element: <Contact /> }],
21+
loader: rootLoader,
22+
action: rootAction,
23+
children: [
24+
{
25+
path: 'contacts/:contactId',
26+
element: <Contact />,
27+
loader: contactLoader,
28+
},
29+
{
30+
path: 'contacts/:contactId/edit',
31+
element: <EditContact />,
32+
loader: contactLoader,
33+
action: editAction
34+
},
35+
],
1936
},
2037
]);
2138

src/routes/contact.jsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
import { Form } from "react-router-dom";
1+
import { Form, useLoaderData } from "react-router-dom";
2+
import { getContact } from "../contacts";
3+
4+
export const loader = async({params}) => {
5+
const contact = await getContact(params.contactId)
6+
return {contact}
7+
}
28

39
export default function Contact() {
4-
const contact = {
5-
first: "Your",
6-
last: "Name",
7-
avatar: "https://placekitten.com/g/200/200",
8-
twitter: "your_handle",
9-
notes: "Some notes",
10-
favorite: true,
11-
};
10+
const {contact} = useLoaderData();
11+
12+
// const contact = {
13+
// first: "Your",
14+
// last: "Name",
15+
// avatar: "https://placekitten.com/g/200/200",
16+
// twitter: "your_handle",
17+
// notes: "Some notes",
18+
// favorite: true,
19+
// };
1220

1321
return (
1422
<div id="contact">

src/routes/edit.jsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Form, useLoaderData, redirect } from "react-router-dom";
2+
import { updateContact } from "../contacts";
3+
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}`)
12+
}
13+
14+
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+
);
69+
}

src/routes/root.jsx

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,69 @@
1-
import { Outlet, Link, useLoaderData } from "react-router-dom";
2-
import { getContacts } from "../contacts";
1+
import { Outlet, NavLink, useLoaderData, Form } from 'react-router-dom';
2+
import { getContacts, createContact } from '../contacts';
33

4-
export async function loader() {
5-
const contacts = await getContacts();
6-
return { contacts };
7-
}
4+
export const loader = async () => {
5+
const contacts = await getContacts();
6+
return { contacts };
7+
};
8+
9+
export const action = async () => {
10+
const contact = await createContact();
11+
return { contact };
12+
};
813

914
export default function Root() {
10-
const {contacts} = useLoaderData()
11-
return (
12-
<>
13-
<div id="sidebar">
14-
<h1>React Router Contacts</h1>
15-
<div>
16-
<form id="search-form" role="search">
17-
<input
18-
id="q"
19-
aria-label="Search contacts"
20-
placeholder="Search"
21-
type="search"
22-
name="q"
23-
/>
24-
<div
25-
id="search-spinner"
26-
aria-hidden
27-
hidden={true}
28-
/>
29-
<div
30-
className="sr-only"
31-
aria-live="polite"
32-
></div>
33-
</form>
34-
<form method="post">
35-
<button type="submit">New</button>
36-
</form>
37-
</div>
38-
{contacts.length ? (
39-
<ul>
40-
{contacts.map((contact) => (
41-
<li key={contact.id}>
42-
<Link to={`contacts/${contact.id}`}>
43-
{contact.first || contact.last ? (
44-
<>
45-
{contact.first} {contact.last}
46-
</>
47-
) : (
48-
<i>No Name</i>
49-
)}{" "}
50-
{contact.favorite && <span></span>}
51-
</Link>
52-
</li>
53-
))}
54-
</ul>
55-
) : (
56-
<p>
57-
<i>No contacts</i>
58-
</p>
59-
)}
60-
</div>
61-
<div id="detail">
62-
<Outlet />
63-
</div>
64-
</>
65-
);
66-
}
15+
const { contacts } = useLoaderData();
16+
return (
17+
<>
18+
<div id='sidebar'>
19+
<h1>React Router Contacts</h1>
20+
<div>
21+
<form id='search-form' role='search'>
22+
<input
23+
id='q'
24+
aria-label='Search contacts'
25+
placeholder='Search'
26+
type='search'
27+
name='q'
28+
/>
29+
<div id='search-spinner' aria-hidden hidden={true} />
30+
<div className='sr-only' aria-live='polite'></div>
31+
</form>
32+
<Form method='post'>
33+
<button type='submit'>New</button>
34+
</Form>
35+
</div>
36+
{contacts.length ? (
37+
<ul>
38+
{contacts.map((contact) => (
39+
<li key={contact.id}>
40+
<NavLink
41+
to={`contacts/${contact.id}`}
42+
className={({ isActive, isPending }) =>
43+
isActive ? 'active' : isPending ? 'peding' : ''
44+
}
45+
>
46+
{contact.first || contact.last ? (
47+
<>
48+
{contact.first} {contact.last}
49+
</>
50+
) : (
51+
<i>No Name</i>
52+
)}{' '}
53+
{contact.favorite && <span></span>}
54+
</NavLink>
55+
</li>
56+
))}
57+
</ul>
58+
) : (
59+
<p>
60+
<i>No contacts</i>
61+
</p>
62+
)}
63+
</div>
64+
<div id='detail'>
65+
<Outlet />
66+
</div>
67+
</>
68+
);
69+
}

0 commit comments

Comments
 (0)