Skip to content

Commit 0fe1465

Browse files
chantastickentcdodds
authored andcommitted
migrate: exercise 06 to kcd-workshop format
1 parent 3d3c2c7 commit 0fe1465

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1022
-374
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# useEffect: HTTP Requests
2+
3+
In this exercise, we'll be doing data fetching directly in a useEffect hook
4+
callback within our component.
5+
6+
Here we have a form where users can enter the name of a pokemon and fetch data
7+
about that pokemon. Your job will be to create a component which makes that
8+
fetch request. When the user submits a pokemon name, our `PokemonInfo` component
9+
will get re-rendered with the `pokemonName`
10+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "/pokemon.css"

src/exercise/06.tsx renamed to exercises/06.use-effect-http-requests/01.problem/index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
// useEffect: HTTP requests
2-
// http://localhost:3000/isolated/exercise/06.tsx
3-
41
import * as React from 'react'
2+
import * as ReactDOM from 'react-dom/client'
53
// 🐨 you'll want the following additional things from '../pokemon':
64
// fetchPokemon: the function we call to get the pokemon info
75
// PokemonInfoFallback: the thing we show while we're loading the pokemon info
86
// PokemonDataView: the stuff we use to display the pokemon info
9-
import {PokemonForm} from '../pokemon'
7+
import {PokemonForm} from '~/shared/pokemon'
108

119
function PokemonInfo({pokemonName}: {pokemonName: string}) {
1210
// 🐨 Have state for the pokemon (null)
@@ -47,4 +45,6 @@ function App() {
4745
)
4846
}
4947

50-
export {App}
48+
const rootEl = document.createElement('div')
49+
document.body.append(rootEl)
50+
ReactDOM.createRoot(rootEl).render(<App />)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# useEffect: HTTP Requests
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "/pokemon.css"

src/final/06.tsx renamed to exercises/06.use-effect-http-requests/01.solution/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
// useEffect: HTTP requests
2-
// http://localhost:3000/isolated/final/06.tsx
3-
41
import * as React from 'react'
2+
import * as ReactDOM from 'react-dom/client'
53
import {
64
fetchPokemon,
75
PokemonInfoFallback,
86
PokemonForm,
97
PokemonDataView,
10-
} from '../pokemon'
11-
import type {PokemonData} from '../types'
8+
} from '~/shared/pokemon'
9+
import type {PokemonData} from '~/shared/types'
1210

1311
function PokemonInfo({pokemonName}: {pokemonName: string}) {
1412
const [pokemon, setPokemon] = React.useState<PokemonData | null>(null)
@@ -48,4 +46,6 @@ function App() {
4846
)
4947
}
5048

51-
export {App}
49+
const rootEl = document.createElement('div')
50+
document.body.append(rootEl)
51+
ReactDOM.createRoot(rootEl).render(<App />)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Handle Errors
2+
3+
Unfortunately, sometimes things go wrong and we need to handle errors when they
4+
do so we can show the user useful information. Handle that error and render it
5+
out like so:
6+
7+
```jsx
8+
<div role="alert">
9+
There was an error: <pre style={{whiteSpace: 'normal'}}>{error.message}</pre>
10+
</div>
11+
```
12+
13+
You can make an error happen by typing an incorrect pokemon name into the input.
14+
15+
One common question I get about this extra credit is how to handle promise
16+
errors. There are two ways to do it in this extra credit:
17+
18+
```javascript
19+
// option 1: using .catch
20+
fetchPokemon(pokemonName)
21+
.then(pokemon => setPokemon(pokemon))
22+
.catch(error => setError(error))
23+
24+
// option 2: using the second argument to .then
25+
fetchPokemon(pokemonName).then(
26+
pokemon => setPokemon(pokemon),
27+
error => setError(error),
28+
)
29+
```
30+
31+
These are functionally equivalent for our purposes, but they are semantically
32+
different in general.
33+
34+
Using `.catch` means that you'll handle an error in the `fetchPokemon` promise,
35+
but you'll _also_ handle an error in the `setPokemon(pokemon)` call as well.
36+
This is due to the semantics of how promises work.
37+
38+
Using the second argument to `.then` means that you will catch an error that
39+
happens in `fetchPokemon` only. In this case, I knew that calling `setPokemon`
40+
would not throw an error (React handles errors and we have an API to catch those
41+
which we'll use later), so I decided to go with the second argument option.
42+
43+
However, in this situation, it doesn't really make much of a difference. If you
44+
want to go with the safe option, then opt for `.catch`.
45+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "/pokemon.css"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as React from 'react'
2+
import * as ReactDOM from 'react-dom/client'
3+
import {
4+
fetchPokemon,
5+
PokemonInfoFallback,
6+
PokemonForm,
7+
PokemonDataView,
8+
} from '~/shared/pokemon'
9+
import type {PokemonData} from '~/shared/types'
10+
11+
function PokemonInfo({pokemonName}: {pokemonName: string}) {
12+
const [pokemon, setPokemon] = React.useState<PokemonData | null>(null)
13+
14+
React.useEffect(() => {
15+
if (!pokemonName) {
16+
return
17+
}
18+
setPokemon(null)
19+
fetchPokemon(pokemonName).then(pokemon => setPokemon(pokemon))
20+
}, [pokemonName])
21+
22+
if (!pokemonName) {
23+
return <span>Submit a pokemon</span>
24+
} else if (!pokemon) {
25+
return <PokemonInfoFallback name={pokemonName} />
26+
} else {
27+
return <PokemonDataView pokemon={pokemon} />
28+
}
29+
}
30+
31+
function App() {
32+
const [pokemonName, setPokemonName] = React.useState('')
33+
34+
function handleSubmit(newPokemonName: string) {
35+
setPokemonName(newPokemonName)
36+
}
37+
38+
return (
39+
<div className="pokemon-info-app">
40+
<PokemonForm pokemonName={pokemonName} onSubmit={handleSubmit} />
41+
<hr />
42+
<div className="pokemon-info">
43+
<PokemonInfo pokemonName={pokemonName} />
44+
</div>
45+
</div>
46+
)
47+
}
48+
49+
const rootEl = document.createElement('div')
50+
document.body.append(rootEl)
51+
ReactDOM.createRoot(rootEl).render(<App />)

0 commit comments

Comments
 (0)