Skip to content

Commit

Permalink
improve instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed May 20, 2024
1 parent 258e80b commit aad2af9
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export async function SearchResults() {
{ key: ship.name },
h(
SelectShipLink,
{ shipId: ship.id, highlight: ship.id === currentShipId },
// 💣 you can remove the search prop here now that this information is
// in our router.
{ shipId: ship.id, search, highlight: ship.id === currentShipId },
h(ShipImg, {
src: getImageUrlForShip(ship.id, { size: 20 }),
alt: ship.name,
Expand Down
17 changes: 14 additions & 3 deletions exercises/04.router/01.problem.router/ui/ship-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,26 @@ export function ShipSearch({ search, results, fallback }) {
)
}

export function SelectShipLink({ shipId, highlight, children }) {
// 💣 you can remove the search prop here now that we can use the location from
// the router
export function SelectShipLink({ shipId, search, highlight, children }) {
// 🐨 get the current location from useRouter

// 🦉 the useLinkHandler you'll add in ui/index.js will set up an event handler
// to listen to clicks to anchor elements and navigate properly.

// right now we're merging manually, but now you can use our
// mergeLocationState utility.
// 🐨 update href to be mergeLocationState(location, { shipId })
const href = [
`/${shipId}`,
search ? `search=${encodeURIComponent(search)}` : null,
]
.filter(Boolean)
.join('?')
return h('a', {
children,
// 🐨 update href to be mergeLocationState(location, { shipId })
href: `/${shipId}`,
href,
style: { fontWeight: highlight ? 'bold' : 'normal' },
})
}
Expand Down
4 changes: 2 additions & 2 deletions exercises/04.router/04.problem.history/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ listen for this event and update the application state accordingly.
function handlePopState() {
// do stuff
}
window.addEventListener(handlePopState)
window.addEventListener('popstate', handlePopState)

// then to clean up
window.removeEventListener(handlePopState)
window.removeEventListener('popstate', handlePopState)
```

Please add this event listener in a `useEffect` in our router logic.
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,9 @@ export async function load(url, context, defaultLoad) {
const result = await reactLoad(url, context, (u, c) => {
return textLoad(u, c, defaultLoad)
})
// 💰 uncomment this to see how our loader transforms the actions file
// if (url.includes('actions.js')) {
// console.log(result.source)
// }
return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ import { EditableText } from './edit-text.js'
import { getImageUrlForShip } from './img-utils.js'
import { ShipImg } from './img.js'

// 💰 you can log what extra properties the updateShipName function gets because
// it's in a 'use server' file:
// const properties = {}
// for (const [key, descriptor] of Object.entries(
// Object.getOwnPropertyDescriptors(updateShipName),
// )) {
// properties[key] = descriptor.value
// }

// console.log(updateShipName.toString())
// console.log(
// JSON.stringify(
// properties,
// (key, value) => (typeof value === 'object' ? value : String(value)),
// 2,
// ),
// )

export async function ShipDetails() {
const { shipId } = shipDataStorage.getStore()
const ship = await getShip({ shipId })
Expand Down
38 changes: 37 additions & 1 deletion exercises/05.actions/01.solution.action-reference/README.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,42 @@
# Action Reference

👨‍💼 Great! So this is what a reference looks like in the client:
👨‍💼 Great! So this is what our Node.js loader does to the `actions.js` module:

```
'use server'
import * as db from '../db/ship-api.js'
export async function updateShipName(previousState, formData) {
try {
await db.updateShipName({
shipId: formData.get('shipId'),
shipName: formData.get('shipName'),
})
return { status: 'success', message: 'Success!' }
} catch (error) {
return { status: 'error', message: error?.message || String(error) }
}
}
;import {registerServerReference} from "react-server-dom-esm/server";
registerServerReference(updateShipName,"file:///Users/kentcdodds/code/epicweb-dev/react-server-components/playground/ui/actions.js","updateShipName");
```

The `registerServerReference` function attaches this additional information onto
our `updateShipName` function:

```json
{
"$$typeof": "Symbol(react.server.reference)",
"$$id": "file:///Users/kentcdodds/code/epicweb-dev/react-server-components/playground/ui/actions.js#updateShipName",
"$$bound": null,
"bind": "function bind() {\n // $FlowFixMe[unsupported-syntax]\n var newFn = FunctionBind.apply(this, arguments);\n\n if (this.$$typeof === SERVER_REFERENCE_TAG) {\n // $FlowFixMe[method-unbinding]\n var args = ArraySlice.call(arguments, 1);\n return Object.defineProperties(newFn, {\n $$typeof: {\n value: SERVER_REFERENCE_TAG\n },\n $$id: {\n value: this.$$id\n },\n $$bound: {\n value: this.$$bound ? this.$$bound.concat(args) : args\n },\n bind: {\n value: bind\n }\n });\n }\n\n return newFn;\n}"
}
```

The serialized version of this function looks like this in our RSC payload:

```
d:{"id":"file:///Users/kentcdodds/code/epicweb-dev/react-server-components/playground/ui/actions.js#updateShipName","bound":null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ export async function load(url, context, defaultLoad) {
const result = await reactLoad(url, context, (u, c) => {
return textLoad(u, c, defaultLoad)
})
if (url.includes('actions.js')) {
console.log(result.source)
}
return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ import { EditableText } from './edit-text.js'
import { getImageUrlForShip } from './img-utils.js'
import { ShipImg } from './img.js'

const properties = {}
for (const [key, descriptor] of Object.entries(
Object.getOwnPropertyDescriptors(updateShipName),
)) {
properties[key] = descriptor.value
}

console.log(updateShipName.toString())
console.log(
JSON.stringify(
properties,
(key, value) => (typeof value === 'object' ? value : String(value)),
2,
),
)

export async function ShipDetails() {
const { shipId } = shipDataStorage.getStore()
const ship = await getShip({ shipId })
Expand Down
10 changes: 10 additions & 0 deletions exercises/05.actions/03.problem.server/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
👨‍💼 Now that we send that reference and any arguments to the backend when we
submit the form. We need an endpoint that will handle that POST request.

The POST request we're making includes the id of the action in the `rsc-action`
header:

```
file:///Users/kentcdodds/code/epicweb-dev/react-server-components/playground/ui/actions.js#updateShipName
```

We can use that to find the module and module function to call in our server
handler.

We'll be using a few server-side modules for parsing the form submission data.
As this is not a Node.js/hono.js course, some of this will be given to you. You
will be responsible for parsing out the action file to `import` and calling it
Expand Down

0 comments on commit aad2af9

Please sign in to comment.