Skip to content

Commit

Permalink
sync external
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Feb 23, 2024
1 parent 3c2040f commit f456fd7
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 16 deletions.
30 changes: 14 additions & 16 deletions exercises/08.focus/01.solution/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useRef, useState } from 'react'
import { flushSync } from 'react-dom'
import * as ReactDOM from 'react-dom/client'

function EditableText({
Expand All @@ -18,19 +17,18 @@ function EditableText({
const [edit, setEdit] = useState(false)
const [value, setValue] = useState(initialValue)
const inputRef = useRef<HTMLInputElement>(null)
const buttonRef = useRef<HTMLButtonElement>(null)
// 🐨 add a button ref here

return edit ? (
<form
method="post"
onSubmit={event => {
event.preventDefault()
// here's where you'd send the updated value to the server
flushSync(() => {
setValue(inputRef.current?.value ?? '')
setEdit(false)
})
buttonRef.current?.focus()
// 🐨 wrap these calls in a flushSync
setValue(inputRef.current?.value ?? '')
setEdit(false)
// 🐨 after flushSync, focus the button with the button ref
}}
>
<input
Expand All @@ -43,28 +41,28 @@ function EditableText({
defaultValue={value}
onKeyDown={event => {
if (event.key === 'Escape') {
flushSync(() => {
setEdit(false)
})
buttonRef.current?.focus()
// 🐨 wrap this in a flushSync
setEdit(false)
// 🐨 after the flushSync, focus the button
}
}}
onBlur={event => {
// 🐨 wrap these in a flushSync
setValue(event.currentTarget.value)
setEdit(false)
// 🐨 after the flushSync, focus the button
}}
/>
</form>
) : (
<button
aria-label={buttonLabel}
// 🐨 add a ref prop for the button
type="button"
ref={buttonRef}
onClick={() => {
flushSync(() => {
setEdit(true)
})
inputRef.current?.select()
// 🐨 wrap this in a flushSync
setEdit(true)
// 🐨 after the flushSync, select all the text of the input
}}
>
{value || 'Edit'}
Expand Down
1 change: 1 addition & 0 deletions exercises/09.sync-external/01.problem/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# useSyncExternalStore
24 changes: 24 additions & 0 deletions exercises/09.sync-external/01.problem/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
main {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 3rem;
}
.editable-text {
button {
/* remove button styles. Make it look like text */
background: none;
border: none;
padding: 4px 8px;
font-size: 1.5rem;
font-weight: bold;
}

input {
/* make it the same size as the button */
font-size: 1.5rem;
font-weight: bold;
padding: 4px 8px;
border: none;
}
}
14 changes: 14 additions & 0 deletions exercises/09.sync-external/01.problem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as ReactDOM from 'react-dom/client'

function NarrowScreenNotifier() {
const isNarrow = false
return isNarrow ? 'You are on a narrow screen' : 'You are on a wide screen'
}

function App() {
return <NarrowScreenNotifier />
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
1 change: 1 addition & 0 deletions exercises/09.sync-external/01.solution/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# useSyncExternalStore
24 changes: 24 additions & 0 deletions exercises/09.sync-external/01.solution/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
main {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 3rem;
}
.editable-text {
button {
/* remove button styles. Make it look like text */
background: none;
border: none;
padding: 4px 8px;
font-size: 1.5rem;
font-weight: bold;
}

input {
/* make it the same size as the button */
font-size: 1.5rem;
font-weight: bold;
padding: 4px 8px;
border: none;
}
}
28 changes: 28 additions & 0 deletions exercises/09.sync-external/01.solution/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useSyncExternalStore } from 'react'
import * as ReactDOM from 'react-dom/client'

const mediaQuery = '(max-width: 600px)'
function getSnapshot() {
return window.matchMedia(mediaQuery).matches
}

function subscribe(callback: () => void) {
const mediaQueryList = window.matchMedia(mediaQuery)
mediaQueryList.addEventListener('change', callback)
return () => {
mediaQueryList.removeEventListener('change', callback)
}
}

function NarrowScreenNotifier() {
const isNarrow = useSyncExternalStore(subscribe, getSnapshot)
return isNarrow ? 'You are on a narrow screen' : 'You are on a wide screen'
}

function App() {
return <NarrowScreenNotifier />
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
1 change: 1 addition & 0 deletions exercises/09.sync-external/02.problem/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Make Store Utility
24 changes: 24 additions & 0 deletions exercises/09.sync-external/02.problem/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
main {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 3rem;
}
.editable-text {
button {
/* remove button styles. Make it look like text */
background: none;
border: none;
padding: 4px 8px;
font-size: 1.5rem;
font-weight: bold;
}

input {
/* make it the same size as the button */
font-size: 1.5rem;
font-weight: bold;
padding: 4px 8px;
border: none;
}
}
28 changes: 28 additions & 0 deletions exercises/09.sync-external/02.problem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useSyncExternalStore } from 'react'
import * as ReactDOM from 'react-dom/client'

const mediaQuery = '(max-width: 600px)'
function getSnapshot() {
return window.matchMedia(mediaQuery).matches
}

function subscribe(callback: () => void) {
const mediaQueryList = window.matchMedia(mediaQuery)
mediaQueryList.addEventListener('change', callback)
return () => {
mediaQueryList.removeEventListener('change', callback)
}
}

function NarrowScreenNotifier() {
const isNarrow = useSyncExternalStore(subscribe, getSnapshot)
return isNarrow ? 'You are on a narrow screen' : 'You are on a wide screen'
}

function App() {
return <NarrowScreenNotifier />
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
1 change: 1 addition & 0 deletions exercises/09.sync-external/02.solution/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Make Store Utility
24 changes: 24 additions & 0 deletions exercises/09.sync-external/02.solution/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
main {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 3rem;
}
.editable-text {
button {
/* remove button styles. Make it look like text */
background: none;
border: none;
padding: 4px 8px;
font-size: 1.5rem;
font-weight: bold;
}

input {
/* make it the same size as the button */
font-size: 1.5rem;
font-weight: bold;
padding: 4px 8px;
border: none;
}
}
21 changes: 21 additions & 0 deletions exercises/09.sync-external/02.solution/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useSyncExternalStore } from 'react'
import * as ReactDOM from 'react-dom/client'
import { makeMediaQueryStore } from './narrow-media-query'

const narrowScreenStore = makeMediaQueryStore('(max-width: 600px)')

function NarrowScreenNotifier() {
const isNarrow = useSyncExternalStore(
narrowScreenStore.subscribe,
narrowScreenStore.getSnapshot,
)
return isNarrow ? 'You are on a narrow screen' : 'You are on a wide screen'
}

function App() {
return <NarrowScreenNotifier />
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
ReactDOM.createRoot(rootEl).render(<App />)
15 changes: 15 additions & 0 deletions exercises/09.sync-external/02.solution/narrow-media-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function makeMediaQueryStore(mediaQuery: string) {
function getSnapshot() {
return window.matchMedia(mediaQuery).matches
}

function subscribe(callback: () => void) {
const mediaQueryList = window.matchMedia(mediaQuery)
mediaQueryList.addEventListener('change', callback)
return () => {
mediaQueryList.removeEventListener('change', callback)
}
}

return { subscribe, getSnapshot }
}
1 change: 1 addition & 0 deletions exercises/09.sync-external/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Sync External State

0 comments on commit f456fd7

Please sign in to comment.