Skip to content

Commit

Permalink
some improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Apr 2, 2024
1 parent 5f97aeb commit d852a49
Show file tree
Hide file tree
Showing 317 changed files with 4,436 additions and 236 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ Learn all about the workshop app on the

[![Kent with the workshop app in the background](https://github-production-user-asset-6210df.s3.amazonaws.com/1500684/280407082-0e012138-e01d-45d5-abf2-86ffe5d03c69.png)](https://www.epicweb.dev/get-started)

## Credits

This workshop is heavily influenced by
[@sebmarkbage](https://github.com/sebmarkbage)'s work in
[this commit](https://github.com/facebook/react/commit/f181ba8aa6339d62f6e2572109c61242606f16b3).
Also, shout-out to [@gaearon](https://github.com/gaearon) and
[@rickhanlonii](https://github.com/rickhanlonii) for their help on this as well.

<!-- prettier-ignore-start -->
[npm]: https://www.npmjs.com/
[node]: https://nodejs.org
Expand Down
3 changes: 1 addition & 2 deletions exercises/01.init/01.problem.static/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"imports": {
"react": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev",
"react-error-boundary": "https://esm.sh/[email protected]?pin=126&dev"
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev"
}
}
</script> -->
Expand Down
10 changes: 4 additions & 6 deletions exercises/01.init/01.problem.static/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@ app.use((req, res, next) => {
// try {
// const shipId = req.params.shipId || null
// const search = req.query.search || ''
// return res.json({
// shipId,
// search,
// ship: shipId ? await getShip({ shipId }) : null,
// shipResults: await searchShips({ search }),
// })
// const ship = shipId ? await getShip({ shipId }) : null
// const shipResults = await searchShips({ search })
// const data = { shipId, search, ship, shipResults }
// return res.json(data)
// } catch (error) {
// console.error(error)
// res.status(500).json({ error: error.message })
Expand Down
3 changes: 1 addition & 2 deletions exercises/01.init/01.solution.static/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
"imports": {
"react": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev",
"react-error-boundary": "https://esm.sh/[email protected]?pin=126&dev"
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev"
}
}
</script>
Expand Down
10 changes: 4 additions & 6 deletions exercises/01.init/01.solution.static/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ app.get('/api/:shipId?', async function (req, res) {
try {
const shipId = req.params.shipId || null
const search = req.query.search || ''
return res.json({
shipId,
search,
ship: shipId ? await getShip({ shipId }) : null,
shipResults: await searchShips({ search }),
})
const ship = shipId ? await getShip({ shipId }) : null
const shipResults = await searchShips({ search })
const data = { shipId, search, ship, shipResults }
return res.json(data)
} catch (error) {
console.error(error)
res.status(500).json({ error: error.message })
Expand Down
6 changes: 6 additions & 0 deletions exercises/02.server-components/01.problem.rsc/README.mdx
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
# RSCs

👨‍💼 Right now we have to load all the data of our app and pass the result as
props to our components. Additionally, we're sending a bunch of code to the
client to render a page that really doesn't need to be on the client. We want
you to solve both of these problems by using `react-server-dom-esm` which will
allow us to generated serialized JSX on the server and render it in the browser.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
"imports": {
"react": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev",
"react-error-boundary": "https://esm.sh/[email protected]?pin=126&dev"
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev"
}
}
</script>
Expand Down
10 changes: 4 additions & 6 deletions exercises/02.server-components/01.problem.rsc/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ app.get('/api/:shipId?', async function (req, res) {
try {
const shipId = req.params.shipId || null
const search = req.query.search || ''
return res.json({
shipId,
search,
ship: shipId ? await getShip({ shipId }) : null,
shipResults: await searchShips({ search }),
})
const ship = shipId ? await getShip({ shipId }) : null
const shipResults = await searchShips({ search })
const data = { shipId, search, ship, shipResults }
return res.json(data)
} catch (error) {
console.error(error)
res.status(500).json({ error: error.message })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"react": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev",
"react-error-boundary": "https://esm.sh/[email protected]?pin=126&dev",
"react-server-dom-esm/client": "/js/react-server-dom-esm/client"
"react-server-dom-esm/client": "https://esm.sh/@kentcdodds/[email protected]/client?pin=v126&dev"
}
}
</script>
Expand Down
21 changes: 5 additions & 16 deletions exercises/02.server-components/01.solution.rsc/server/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { createRequire } from 'node:module'
import path from 'node:path'
import closeWithGrace from 'close-with-grace'
import compress from 'compression'
import express from 'express'
import { createElement as h } from 'react'
import { renderToPipeableStream } from 'react-server-dom-esm/server'
import { getShip, searchShips } from '../db/ship-api.js'
import { App } from '../src/app.js'

const PORT = process.env.PORT || 3000
Expand All @@ -17,19 +16,6 @@ app.head('/', (req, res) => res.status(200).end())
app.use(express.static('public', { index: false }))
app.use('/js/src', express.static('src'))

// we have to server this file from our own server so dynamic imports are
// relative to our own server (this module is what loads client-side modules!)
app.use('/js/react-server-dom-esm/client', (req, res) => {
const require = createRequire(import.meta.url)
const pkgPath = require.resolve('react-server-dom-esm')
const modulePath = path.join(
path.dirname(pkgPath),
'esm',
'react-server-dom-esm-client.browser.development.js',
)
res.sendFile(modulePath)
})

// This just cleans up the URL if the search ever gets cleared... Not important
// for RSCs... Just ... I just can't help myself. I like URLs clean.
app.use((req, res, next) => {
Expand All @@ -49,7 +35,10 @@ app.get('/rsc/:shipId?', async function (req, res) {
try {
const shipId = req.params.shipId || null
const search = req.query.search || ''
const { pipe } = renderToPipeableStream(h(App, { shipId, search }))
const ship = shipId ? await getShip({ shipId }) : null
const shipResults = await searchShips({ search })
const props = { shipId, search, ship, shipResults }
const { pipe } = renderToPipeableStream(h(App, props))
pipe(res)
} catch (error) {
console.error(error)
Expand Down
6 changes: 3 additions & 3 deletions exercises/02.server-components/01.solution.rsc/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Fragment, createElement as h } from 'react'
import { ShipDetails } from './ship-details.js'
import { SearchResults } from './ship-search-results.js'

export async function App({ shipId, search }) {
export function App({ shipId, search, ship, shipResults }) {
return h(
'div',
{ className: 'app' },
Expand All @@ -23,14 +23,14 @@ export async function App({ shipId, search }) {
autoFocus: true,
}),
),
h('ul', null, h(SearchResults, { shipId, search })),
h('ul', null, h(SearchResults, { shipId, search, shipResults })),
),
),
h(
'div',
{ className: 'details' },
shipId
? h(ShipDetails, { shipId })
? h(ShipDetails, { shipId, ship })
: h('p', null, 'Select a ship from the list to see details'),
),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { createElement as h } from 'react'
import { getShip } from '../db/ship-api.js'
import { getImageUrlForShip } from './img-utils.js'

export async function ShipDetails({ shipId }) {
const ship = await getShip({ shipId })
export function ShipDetails({ ship }) {
const shipImgSrc = getImageUrlForShip(ship.id, { size: 200 })
return h(
'div',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { createElement as h } from 'react'
import { searchShips } from '../db/ship-api.js'
import { getImageUrlForShip } from './img-utils.js'

export async function SearchResults({ shipId: currentShipId, search }) {
const shipResults = await searchShips({ search })
export function SearchResults({ shipId: currentShipId, shipResults, search }) {
return shipResults.ships.map(ship => {
const href = [
`/${ship.id}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Async Components
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "exercises__sep__02.server-components__sep__02.problem.streaming",
"name": "exercises__sep__02.server-components__sep__02.problem.async-components",
"version": "1.0.0",
"type": "module",
"private": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"react": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom": "https://esm.sh/[email protected]?pin=v126&dev",
"react-dom/client": "https://esm.sh/[email protected]/client?pin=v126&dev",
"react-error-boundary": "https://esm.sh/[email protected]?pin=126&dev",
"react-server-dom-esm/client": "/js/react-server-dom-esm/client"
"react-server-dom-esm/client": "https://esm.sh/@kentcdodds/[email protected]/client?pin=v126&dev"
}
}
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import closeWithGrace from 'close-with-grace'
import compress from 'compression'
import express from 'express'
import { createElement as h } from 'react'
import { renderToPipeableStream } from 'react-server-dom-esm/server'
import { getShip, searchShips } from '../db/ship-api.js'
import { App } from '../src/app.js'

const PORT = process.env.PORT || 3000

const app = express()
app.use(compress())
// this is here so the workshop app knows when the server has started
app.head('/', (req, res) => res.status(200).end())

app.use(express.static('public', { index: false }))
app.use('/js/src', express.static('src'))

// This just cleans up the URL if the search ever gets cleared... Not important
// for RSCs... Just ... I just can't help myself. I like URLs clean.
app.use((req, res, next) => {
if (req.query.search === '') {
const searchParams = new URLSearchParams(req.search)
searchParams.delete('search')
const location = [req.path, searchParams.toString()]
.filter(Boolean)
.join('?')
return res.redirect(302, location)
} else {
next()
}
})

app.get('/rsc/:shipId?', async function (req, res) {
try {
const shipId = req.params.shipId || null
const search = req.query.search || ''
const ship = shipId ? await getShip({ shipId }) : null
const shipResults = await searchShips({ search })
const props = { shipId, search, ship, shipResults }
const { pipe } = renderToPipeableStream(h(App, props))
pipe(res)
} catch (error) {
console.error(error)
res.status(500).json({ error: error.message })
}
})

app.get('/:shipId?', async function (req, res) {
res.set('Content-type', 'text/html')
return res.sendFile('index.html', { root: 'public' })
})

const server = app.listen(PORT, () => {
console.log(`🚀 We have liftoff!`)
console.log(`http://localhost:${PORT}`)
})

closeWithGrace(async ({ signal, err }) => {
if (err) console.error('Shutting down server due to error', err)
else console.log('Shutting down server due to signal', signal)

await new Promise((resolve, reject) => {
server.close(err => {
if (err) reject(err)
else resolve()
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Fragment, createElement as h } from 'react'
import { ShipDetails } from './ship-details.js'
import { SearchResults } from './ship-search-results.js'

export function App({ shipId, search, ship, shipResults }) {
return h(
'div',
{ className: 'app' },
h(
'div',
{ className: 'search' },
h(
Fragment,
null,
h(
'form',
{},
h('input', {
name: 'search',
placeholder: 'Filter ships...',
type: 'search',
defaultValue: search,
autoFocus: true,
}),
),
h('ul', null, h(SearchResults, { shipId, search, shipResults })),
),
),
h(
'div',
{ className: 'details' },
shipId
? h(ShipDetails, { shipId, ship })
: h('p', null, 'Select a ship from the list to see details'),
),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createElement as h } from 'react'
import { getImageUrlForShip } from './img-utils.js'

export function ShipDetails({ ship }) {
const shipImgSrc = getImageUrlForShip(ship.id, { size: 200 })
return h(
'div',
{ className: 'ship-info' },
h(
'div',
{ className: 'ship-info__img-wrapper' },
h('img', { src: shipImgSrc, alt: ship.name }),
),
h('section', null, h('h2', null, ship.name)),
h('div', null, 'Top Speed: ', ship.topSpeed, ' ', h('small', null, 'lyh')),
h(
'section',
null,
ship.weapons.length
? h(
'ul',
null,
ship.weapons.map(weapon =>
h(
'li',
{ key: weapon.name },
h('label', null, weapon.name),
':',
' ',
h(
'span',
null,
weapon.damage,
' ',
h('small', null, '(', weapon.type, ')'),
),
),
),
)
: h('p', null, 'NOTE: This ship is not equipped with any weapons.'),
),
)
}
Loading

0 comments on commit d852a49

Please sign in to comment.