Skip to content

Commit edf0982

Browse files
committed
better starting
1 parent 2ea61d1 commit edf0982

File tree

6 files changed

+175
-7
lines changed

6 files changed

+175
-7
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {
2+
Navigate,
3+
Route,
4+
RouterProvider,
5+
createBrowserRouter,
6+
createRoutesFromElements,
7+
} from 'react-router-dom'
8+
9+
// Layouts
10+
import { MainLayout } from '~/MainLayout'
11+
import { VacationsSubLayout } from '~/VacationsSubLayout'
12+
import { AccountSubLayout } from '~/AccountSubLayout'
13+
14+
// Pages
15+
import { BrowseVacationsPage, loader as browseVacationsLoader } from './BrowseVacationsPage'
16+
import { VacationDetailsPage } from './VacationDetailsPage'
17+
import { LoginPage } from '~/LoginPage'
18+
import { ErrorPage } from '~/ErrorPage'
19+
import { NotFoundPage } from '~/NotFoundPage'
20+
import { AccountHome } from '~/AccountHome'
21+
22+
export const router = createBrowserRouter(
23+
createRoutesFromElements(
24+
<Route Component={MainLayout}>
25+
<Route Component={VacationsSubLayout}>
26+
<Route
27+
index
28+
Component={BrowseVacationsPage}
29+
loader={browseVacationsLoader}
30+
errorElement={<ErrorPage />}
31+
/>
32+
<Route path="vacations">
33+
<Route
34+
path=":vacationId"
35+
Component={VacationDetailsPage}
36+
// loader={vacationDetailsLoader}
37+
errorElement={<ErrorPage />}
38+
/>
39+
<Route path="deal-of-the-day" element={<Navigate to="../3" />} />
40+
<Route index element={<Navigate to="/" />} />
41+
</Route>
42+
</Route>
43+
<Route path="login" Component={LoginPage} />
44+
<Route path="account" Component={AccountSubLayout}>
45+
<Route index Component={AccountHome} />
46+
</Route>
47+
<Route path="*" Component={NotFoundPage} />
48+
</Route>
49+
)
50+
)
51+
52+
export function App() {
53+
return <RouterProvider router={router} />
54+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useLoaderData } from 'react-router-dom'
2+
import { Tiles } from '~/Tiles'
3+
import { api } from '~/utils/api'
4+
import { BrowseVacationsItem } from '~/BrowseVacationsItem'
5+
import { queryClient } from '~/utils/queryClient'
6+
7+
export async function loader() {
8+
const vacations = await queryClient.ensureQueryData({
9+
queryKey: ['vacations'],
10+
queryFn: () => api.vacations.getAll(),
11+
staleTime: 1000 * 30,
12+
})
13+
return vacations
14+
}
15+
16+
export function BrowseVacationsPage() {
17+
const vacations = useLoaderData() as Awaited<ReturnType<typeof loader>>
18+
return (
19+
<div>
20+
{!vacations && <div>Loading...</div>}
21+
{vacations ? (
22+
<Tiles minSize={15}>
23+
{vacations.map((vacation) => {
24+
return (
25+
<div key={vacation.id} className="bg-white border">
26+
<BrowseVacationsItem vacation={vacation} />
27+
</div>
28+
)
29+
})}
30+
</Tiles>
31+
) : null}
32+
</div>
33+
)
34+
}

react/advanced-hooks/01-useEffect/01-lecture-deep-dive/NOTES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
1. Teach useEffect
2+
2. Refactor to React Router loaders
3+
3. Remember to throw for 404
4+
5+
```js
6+
if (!vacation) throw new Response('Not Found', { status: 404 })
7+
```
8+
19
- https://reacttraining.com/blog/useEffect-is-not-the-new-componentDidMount/
210
- https://reacttraining.com/blog/when-to-use-functions-in-hooks-dependency-array/
311
- https://reacttraining.com/blog/setting-state-on-unmounted-component
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useQueries } from '@tanstack/react-query'
2+
import { Link } from 'react-router-dom'
3+
import { api } from '~/utils/api'
4+
import { Tiles } from '~/Tiles'
5+
import { VacationImage } from '~/VacationImage'
6+
import type { Vacation } from '~/utils/types'
7+
8+
type Props = {
9+
vacationIds: number[]
10+
}
11+
12+
export function SimilarVacations({ vacationIds: ids }: Props) {
13+
const vacations = useQueries({
14+
queries: ids.map((id) => {
15+
return {
16+
queryKey: ['vacation', id],
17+
queryFn: () => api.vacations.getVacation(id),
18+
staleTime: 1000 * 30, // 30s
19+
}
20+
}),
21+
})
22+
.filter((results) => results.isSuccess)
23+
.map((results) => results.data) as Vacation[]
24+
25+
return (
26+
<Tiles minSize={7}>
27+
{vacations.map((vacation) => {
28+
return (
29+
<Link key={vacation.id} to={`/vacations/${vacation.id}`} className="relative group">
30+
<span className="bg-slate-800 text-white text-xs absolute top-0 right-0 py-1 px-2 hidden group-hover:block">
31+
{vacation.id}
32+
</span>
33+
<VacationImage
34+
vacationId={vacation.id}
35+
alt={vacation.name}
36+
className="block object-cover h-20 aspect-video"
37+
/>
38+
<b className="block text-sm text-textColor">{vacation.name}</b>
39+
</Link>
40+
)
41+
})}
42+
</Tiles>
43+
)
44+
}

react/advanced-hooks/01-useEffect/01-lecture-deep-dive/VacationDetailsPage.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
import { useEffect, useState } from 'react'
2-
import { useParams } from 'react-router-dom'
2+
import { type LoaderFunctionArgs, useLoaderData, useParams } from 'react-router-dom'
33
import { api } from '~/utils/api'
4+
import { useQuery } from '@tanstack/react-query'
45
import { VacationImage } from '~/VacationImage'
56
import { Heading } from '~/Heading'
6-
import { SimilarVacations } from '~/SimilarVacations'
7+
import { SimilarVacations } from './SimilarVacations'
78
import { Card } from '~/Card'
89
import type { Vacation } from '~/utils/types'
910

1011
// Setting state on unmounted components
1112
// https://github.com/facebook/react/pull/22114
1213

14+
// const vacation = await queryClient.ensureQueryData({
15+
// queryKey: ['vacation', vacationId],
16+
// queryFn: () => api.vacations.getVacation(vacationId),
17+
// staleTime: 1000 * 30,
18+
// })
19+
20+
// export async function loader({ params }: LoaderFunctionArgs) {
21+
// return api.vacations.getVacation(vacationId)
22+
// }
23+
24+
// const { data: vacation } = useQuery({
25+
// queryKey: ['vacation', vacationId],
26+
// queryFn: () => api.vacations.getVacation(vacationId),
27+
// staleTime: 1000 * 30,
28+
// })
29+
1330
export function VacationDetailsPage() {
1431
const { vacationId } = useParams()
1532
const [vacation, setVacation] = useState<Vacation | null>(null)
@@ -53,8 +70,3 @@ export function VacationDetailsPage() {
5370
</Card>
5471
)
5572
}
56-
57-
// ignore this
58-
export async function loader() {
59-
return null
60-
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as ReactDOM from 'react-dom/client'
2+
import { QueryClientProvider } from '@tanstack/react-query'
3+
import { AuthProvider } from '~/AuthContext'
4+
import { FavoriteProvider } from '~/FavoriteContext'
5+
import { queryClient } from '~/utils/queryClient'
6+
import { App } from './App'
7+
8+
ReactDOM.createRoot(document.getElementById('root')!).render(
9+
<QueryClientProvider client={queryClient}>
10+
<AuthProvider>
11+
<FavoriteProvider>
12+
<App />
13+
</FavoriteProvider>
14+
</AuthProvider>
15+
</QueryClientProvider>
16+
)

0 commit comments

Comments
 (0)