Skip to content

Commit 9c4602d

Browse files
committed
adding practice
1 parent 9fa0da3 commit 9c4602d

File tree

10 files changed

+219
-54
lines changed

10 files changed

+219
-54
lines changed

react/core/06-context/practice/GUIDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ formatDate('MM')
3030

3131
If you wish to see other formatting options: https://day.js.org/docs/en/display/format
3232

33-
## What is a compound component?
33+
## What is a compound component?
3434

3535
First, let's understand what a compound component is. It's essentially making one visual component from several React components like this:
3636

Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
1-
import { useState, memo, useTransition, useCallback } from 'react'
2-
import { useUsers, type UserType } from './useUsers'
1+
import { useState, memo, useTransition, useCallback, useDeferredValue } from 'react'
2+
import { useUsers, type UserType } from './helpers/useUsers'
33
import { Card } from '~/Card'
44
import { Heading } from '~/Heading'
55

66
// https://github.com/reactwg/react-18/discussions/41
77
// https://vercel.com/blog/how-react-18-improves-application-performance
88

9-
console.time()
10-
setTimeout(() => {
11-
console.timeEnd()
12-
}, 1000)
13-
14-
export function BrowseUsers() {
15-
const allUsers = useUsers(100) // generate 100 user objects with random "likes"
9+
export function App() {
10+
const allUsers = useUsers(5000)
1611

1712
const [users, setUsers] = useState(allUsers)
1813
const [minLikes, setMinLikes] = useState(0)
1914

20-
// const [pending, startTransition] = useTransition()
21-
function filterUsers(minLikes: number) {
22-
setMinLikes(minLikes)
15+
const [pending, startTransition] = useTransition()
16+
function filterUsers(newMinLikes: number) {
17+
setMinLikes(newMinLikes)
2318

24-
console.time()
25-
const filteredUsers = allUsers?.filter((u) => u.likes >= minLikes)
26-
console.timeEnd()
19+
if (newMinLikes !== minLikes) {
20+
console.log('start')
21+
console.time()
22+
const filteredUsers = allUsers?.filter((u) => u.likes >= newMinLikes)
23+
console.timeEnd()
24+
}
2725

2826
// setUsers(filteredUsers)
2927
}
@@ -53,14 +51,15 @@ export function BrowseUsers() {
5351
/>
5452
<div>
5553
Showing: <b className="text-slate-800">{users?.length}</b> Users
56-
{/* {pending && '...'} */}
54+
{pending && '...'}
5755
</div>
5856
</div>
5957
</header>
6058
<hr />
6159
<div className="space-y-3">
6260
{/* Turn this into <UserList users={users} editUser={editUser} /> */}
63-
{users.map((user) => {
61+
<UserList users={users} />
62+
{/* {users.map((user) => {
6463
return (
6564
<div key={user.id} className="flex gap-6 bg-slate-100 p-4">
6665
<div className="flex-1">{user.name}</div>
@@ -70,31 +69,31 @@ export function BrowseUsers() {
7069
</button>
7170
</div>
7271
)
73-
})}
72+
})} */}
7473
</div>
7574
</Card>
7675
)
7776
}
7877

79-
// type Props = {
80-
// users: UserType[]
81-
// editUser(userId: number): void
82-
// }
83-
//
84-
// const UserList = memo(({ users, editUser }: Props) => {
85-
// return (
86-
// <>
87-
// {users.map((user) => {
88-
// return (
89-
// <div key={user.id} className="flex gap-6 bg-slate-100 p-4">
90-
// <div className="flex-1">{user.name}</div>
91-
// <div className="flex-1">Liked Vacations: {user.likes}</div>
92-
// <button className="button" onClick={() => editUser(user.id)}>
93-
// Edit User
94-
// </button>
95-
// </div>
96-
// )
97-
// })}
98-
// </>
99-
// )
100-
// })
78+
type Props = {
79+
users: UserType[]
80+
// editUser(userId: number): void
81+
}
82+
83+
const UserList = memo(({ users }: Props) => {
84+
// const users2 = useDeferredValue(users)
85+
86+
return (
87+
<>
88+
{users.map((user) => {
89+
return (
90+
<div key={user.id} className="flex gap-6 bg-slate-100 p-4">
91+
<div className="flex-1">{user.name}</div>
92+
<div className="flex-1">Liked Vacations: {user.likes}</div>
93+
<button className="button">Edit User</button>
94+
</div>
95+
)
96+
})}
97+
</>
98+
)
99+
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as ReactDOM from 'react-dom/client'
2+
import { MainLayout } from './helpers/MainLayout'
3+
import { App } from './Transitions'
4+
5+
ReactDOM.createRoot(document.getElementById('root')!).render(
6+
<MainLayout>
7+
<App />
8+
</MainLayout>
9+
)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { useState, useTransition } from 'react'
2+
3+
export function App() {
4+
const [nextTabIndex, setNextTabIndex] = useState(0)
5+
const [tabIndex, setTabIndex] = useState(0)
6+
7+
const [pending, start] = useTransition()
8+
function onChange(index: number) {
9+
setNextTabIndex(index)
10+
start(() => {
11+
setTabIndex(index)
12+
})
13+
}
14+
15+
return (
16+
<div>
17+
<div className="bg-slate-100 rounded-md p-2 mb-5">
18+
<Tab onClick={() => onChange(0)} selected={nextTabIndex === 0}>
19+
Tab
20+
</Tab>
21+
<Tab onClick={() => onChange(1)} selected={nextTabIndex === 1}>
22+
Slow Tab
23+
</Tab>
24+
<Tab onClick={() => onChange(2)} selected={nextTabIndex === 2}>
25+
Tab
26+
</Tab>
27+
</div>
28+
<div>
29+
{!pending ? (
30+
<>
31+
{tabIndex === 0 && <div>First Tab</div>}
32+
{tabIndex === 1 && <SlowContent />}
33+
{tabIndex === 2 && <div>Last Tab</div>}
34+
</>
35+
) : (
36+
'Loading...'
37+
)}
38+
</div>
39+
</div>
40+
)
41+
}
42+
43+
function Tab({ children, selected, onClick }: any) {
44+
return (
45+
<button className={selected ? 'py-3 px-5 bg-slate-300' : 'py-3 px-5'} onClick={onClick}>
46+
{children}
47+
</button>
48+
)
49+
}
50+
51+
function SlowContent() {
52+
return (
53+
<div>
54+
{[...Array(10).keys()].map((n) => (
55+
<Item key={n} />
56+
))}
57+
</div>
58+
)
59+
}
60+
61+
function Item() {
62+
// Artificially slow
63+
const startTime = performance.now()
64+
while (performance.now() - startTime < 300) {}
65+
return <div className="text-red-500">Slow content</div>
66+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useState, useTransition, useDeferredValue } from 'react'
2+
3+
export function App() {
4+
const [tabIndex, setTabIndex] = useState(0)
5+
6+
return (
7+
<div>
8+
<div className="bg-slate-100 rounded-md p-2 mb-5">
9+
<Tab onClick={() => setTabIndex(0)} selected={tabIndex === 0}>
10+
Tab
11+
</Tab>
12+
<Tab onClick={() => setTabIndex(1)} selected={tabIndex === 1}>
13+
Slow Tab
14+
</Tab>
15+
<Tab onClick={() => setTabIndex(2)} selected={tabIndex === 2}>
16+
Tab
17+
</Tab>
18+
</div>
19+
<div>
20+
{tabIndex === 0 && <div>First Tab</div>}
21+
{tabIndex === 1 && <SlowContent />}
22+
{tabIndex === 2 && <div>Last Tab</div>}
23+
</div>
24+
</div>
25+
)
26+
}
27+
28+
function Tab({ children, selected, onClick }: any) {
29+
return (
30+
<button className={selected ? 'py-3 px-5 bg-slate-300' : 'py-3 px-5'} onClick={onClick}>
31+
{children}
32+
</button>
33+
)
34+
}
35+
36+
function SlowContent() {
37+
return (
38+
<div>
39+
{[...Array(10).keys()].map((n) => (
40+
<Item key={n} />
41+
))}
42+
</div>
43+
)
44+
}
45+
46+
function Item() {
47+
// Artificially slow
48+
const startTime = performance.now()
49+
while (performance.now() - startTime < 300) {}
50+
return <div className="text-red-500">Slow content</div>
51+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Rendering Performance
2+
3+
This practice has three tabs where the rendering of the second tab is significantly slower than the others. If you click on the second tab and then immediately click to one the fast tabs you'll have to wait until the slow tab finishes first before you'll see your second clicked tab show.
4+
5+
Decide if `useTransition` or `useDeferredValue` is better suited to make this UI faster
6+
7+
Here is the syntax for each:
8+
9+
```js
10+
/****************************************
11+
useTransition
12+
*****************************************/
13+
const [pending, startTransition] = useTransition()
14+
15+
function someEvent() {
16+
setState() // high priority
17+
startTransition(() => {
18+
setState() // low priority
19+
})
20+
}
21+
22+
/****************************************
23+
useDeferredValue
24+
*****************************************/
25+
const [myState, setMyState] = useState()
26+
const myStateDeferred = useDeferredValue(myState)
27+
```
28+
29+
## Task 1
30+
31+
Make the UI faster by allowing the user to click on the slow tab then immediately click on a fast tab. Clicking the fast tab should show the fast tab soon instead of waiting for the slow tab to finish.
32+
33+
## Bonus Task
34+
35+
When a tab is clicked, it gets a darker background color if you pass a prop of `selected={true}`. Right now that prop is being passed but the user doesn't see the color until the tab is fully rendered. This means if we click on the slow tab, we have to wait a few seconds for it to render but also the tab's background color remains light and there's no visual queue to know we even clicked. See if you can fix this issue.
36+
37+
Hint: you'll need another `useState`
38+
39+
## Bonus Task
40+
41+
When a tab is "loading", like in the case of the slow tab, it would be nice if there was a "Loading..." indicator.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as ReactDOM from 'react-dom/client'
2+
import { LessonBody, LessonCard } from '~/Lesson'
3+
// import { App } from './App.final'
4+
import { App } from './App'
5+
6+
ReactDOM.createRoot(document.getElementById('root')!).render(
7+
<LessonBody>
8+
<LessonCard className="flex-1">
9+
<App />
10+
</LessonCard>
11+
</LessonBody>
12+
)

react/core/08-rendering-performance/useTransition/index.tsx

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)