Skip to content

Commit e06dd63

Browse files
committed
add sync external exercise
1 parent f456fd7 commit e06dd63

File tree

10 files changed

+127
-117
lines changed

10 files changed

+127
-117
lines changed

exercises/09.sync-external/01.problem/index.css

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

exercises/09.sync-external/01.solution/index.css

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

exercises/09.sync-external/02.problem/index.css

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

exercises/09.sync-external/02.solution/index.css

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

exercises/09.sync-external/02.solution/index.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
import { useSyncExternalStore } from 'react'
22
import * as ReactDOM from 'react-dom/client'
3-
import { makeMediaQueryStore } from './narrow-media-query'
43

5-
const narrowScreenStore = makeMediaQueryStore('(max-width: 600px)')
4+
export function makeMediaQueryStore(mediaQuery: string) {
5+
function getSnapshot() {
6+
return window.matchMedia(mediaQuery).matches
7+
}
8+
9+
function subscribe(callback: () => void) {
10+
const mediaQueryList = window.matchMedia(mediaQuery)
11+
mediaQueryList.addEventListener('change', callback)
12+
return () => {
13+
mediaQueryList.removeEventListener('change', callback)
14+
}
15+
}
16+
17+
return function useMediaQuery() {
18+
return useSyncExternalStore(subscribe, getSnapshot)
19+
}
20+
}
21+
22+
const useNarrowMediaQuery = makeMediaQueryStore('(max-width: 600px)')
623

724
function NarrowScreenNotifier() {
8-
const isNarrow = useSyncExternalStore(
9-
narrowScreenStore.subscribe,
10-
narrowScreenStore.getSnapshot,
11-
)
25+
const isNarrow = useNarrowMediaQuery()
1226
return isNarrow ? 'You are on a narrow screen' : 'You are on a wide screen'
1327
}
1428

exercises/09.sync-external/02.solution/narrow-media-query.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Make Store Utility
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { useSyncExternalStore } from 'react'
2+
import * as ReactDOM from 'react-dom/client'
3+
4+
export function makeMediaQueryStore(mediaQuery: string) {
5+
function getSnapshot() {
6+
return window.matchMedia(mediaQuery).matches
7+
}
8+
9+
function subscribe(callback: () => void) {
10+
const mediaQueryList = window.matchMedia(mediaQuery)
11+
mediaQueryList.addEventListener('change', callback)
12+
return () => {
13+
mediaQueryList.removeEventListener('change', callback)
14+
}
15+
}
16+
17+
return function useMediaQuery() {
18+
return useSyncExternalStore(subscribe, getSnapshot)
19+
}
20+
}
21+
22+
const useNarrowMediaQuery = makeMediaQueryStore('(max-width: 600px)')
23+
24+
function NarrowScreenNotifier() {
25+
const isNarrow = useNarrowMediaQuery()
26+
return isNarrow ? 'You are on a narrow screen' : 'You are on a wide screen'
27+
}
28+
29+
function App() {
30+
return (
31+
<div>
32+
<div>This is your narrow screen state:</div>
33+
{/* 🐨 add a Suspense component around this with a fallback prop */}
34+
<NarrowScreenNotifier />
35+
</div>
36+
)
37+
}
38+
39+
const rootEl = document.createElement('div')
40+
document.body.append(rootEl)
41+
// 🦉 here's how we pretend we're server-rendering
42+
rootEl.innerHTML = (await import('react-dom/server')).renderToString(<App />)
43+
44+
// 🦉 here's how we simulate a delay in hydrating with client-side js
45+
await new Promise(resolve => setTimeout(resolve, 1000))
46+
47+
ReactDOM.hydrateRoot(rootEl, <App />, {
48+
// 💯 if you want to silence the error add a onRecoverableError function here
49+
// and if the error includes 'Missing getServerSnapshot' then return early
50+
// otherwise log the error so you don't miss any other errors.
51+
})
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Make Store Utility
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Suspense, useSyncExternalStore } from 'react'
2+
import * as ReactDOM from 'react-dom/client'
3+
4+
export function makeMediaQueryStore(mediaQuery: string) {
5+
function getSnapshot() {
6+
return window.matchMedia(mediaQuery).matches
7+
}
8+
9+
function subscribe(callback: () => void) {
10+
const mediaQueryList = window.matchMedia(mediaQuery)
11+
mediaQueryList.addEventListener('change', callback)
12+
return () => {
13+
mediaQueryList.removeEventListener('change', callback)
14+
}
15+
}
16+
17+
return function useMediaQuery() {
18+
return useSyncExternalStore(subscribe, getSnapshot)
19+
}
20+
}
21+
22+
const useNarrowMediaQuery = makeMediaQueryStore('(max-width: 600px)')
23+
24+
function NarrowScreenNotifier() {
25+
const isNarrow = useNarrowMediaQuery()
26+
return isNarrow ? 'You are on a narrow screen' : 'You are on a wide screen'
27+
}
28+
29+
function App() {
30+
return (
31+
<div>
32+
<div>This is your narrow screen state:</div>
33+
<Suspense fallback="">
34+
<NarrowScreenNotifier />
35+
</Suspense>
36+
</div>
37+
)
38+
}
39+
40+
const rootEl = document.createElement('div')
41+
document.body.append(rootEl)
42+
// 🦉 here's how we pretend we're server-rendering
43+
rootEl.innerHTML = (await import('react-dom/server')).renderToString(<App />)
44+
45+
// 🦉 here's how we simulate a delay in hydrating with client-side js
46+
await new Promise(resolve => setTimeout(resolve, 1000))
47+
48+
ReactDOM.hydrateRoot(rootEl, <App />, {
49+
onRecoverableError(error) {
50+
if (String(error).includes('Missing getServerSnapshot')) return
51+
52+
console.error(error)
53+
},
54+
})

0 commit comments

Comments
 (0)