Skip to content

Commit c479045

Browse files
committed
Handle the feature that favorites
1 parent e4ceb28 commit c479045

File tree

3 files changed

+31
-9
lines changed

3 files changed

+31
-9
lines changed

src/main.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Root, {
99
loader as rootLoader,
1010
action as rootAction,
1111
} from './routes/root';
12-
import { loader as contactLoader } from './routes/contact';
12+
import { loader as contactLoader, action as contactAction } from './routes/contact';
1313
import './index.css';
1414
import EditContact from './routes/edit';
1515
import { action as editAction } from './routes/edit';
@@ -25,10 +25,11 @@ const router = createBrowserRouter([
2525
action: rootAction,
2626
children: [
2727
{ index: true, element: <Index /> },
28-
{
28+
{
2929
path: 'contacts/:contactId',
3030
element: <Contact />,
3131
loader: contactLoader,
32+
action: contactAction
3233
},
3334
{
3435
path: 'contacts/:contactId/edit',

src/routes/contact.jsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
import { Form, useLoaderData } from "react-router-dom";
2-
import { getContact } from "../contacts";
1+
import { Form, useLoaderData, useFetcher } from "react-router-dom";
2+
import { getContact, updateContact } from "../contacts";
33

44
export const loader = async({params}) => {
55
const contact = await getContact(params.contactId)
66
return {contact}
77
}
88

9+
export async function action({ request, params }) {
10+
let formData = await request.formData();
11+
return updateContact(params.contactId, {
12+
favorite: formData.get("favorite") === "true",
13+
});
14+
}
15+
916
export default function Contact() {
1017
const {contact} = useLoaderData();
1118

@@ -79,9 +86,10 @@ export default function Contact() {
7986

8087
function Favorite({ contact }) {
8188
// yes, this is a `let` for later
89+
const fetcher = useFetcher()
8290
let favorite = contact.favorite;
8391
return (
84-
<Form method="post">
92+
<fetcher.Form method="post">
8593
<button
8694
name="favorite"
8795
value={favorite ? "false" : "true"}
@@ -93,6 +101,6 @@ function Favorite({ contact }) {
93101
>
94102
{favorite ? "★" : "☆"}
95103
</button>
96-
</Form>
104+
</fetcher.Form>
97105
);
98106
}

src/routes/root.jsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
useLoaderData,
66
Form,
77
useNavigation,
8+
useSubmit,
89
} from 'react-router-dom';
910
import { getContacts, createContact } from '../contacts';
1011

@@ -23,11 +24,21 @@ export const action = async () => {
2324
export default function Root() {
2425
const { contacts, q } = useLoaderData();
2526
const navigation = useNavigation();
27+
const submit = useSubmit();
2628

29+
const searching =
30+
navigation.location &&
31+
new URLSearchParams(navigation.location.search).has('q');
32+
33+
const changeHandler = (event) => {
34+
const isFirstSearch = q === null;
35+
console.log('isFirstSearch: ', isFirstSearch);
36+
submit(event.currentTarget.form, { replace: !isFirstSearch });
37+
};
2738
// Synchronize the URL and Form state
2839
useEffect(() => {
29-
document.getElementById("q").value = q
30-
}, [q])
40+
document.getElementById('q').value = q;
41+
}, [q]);
3142
return (
3243
<>
3344
<div id='sidebar'>
@@ -36,13 +47,15 @@ export default function Root() {
3647
<Form id='search-form' role='search'>
3748
<input
3849
id='q'
50+
className={searching ? 'loading' : ''}
3951
aria-label='Search contacts'
4052
placeholder='Search'
4153
type='search'
4254
name='q'
4355
defaultValue={q}
56+
onChange={changeHandler}
4457
/>
45-
<div id='search-spinner' aria-hidden hidden={true} />
58+
<div id='search-spinner' aria-hidden hidden={!searching} />
4659
<div className='sr-only' aria-live='polite'></div>
4760
</Form>
4861
<Form method='post'>

0 commit comments

Comments
 (0)