useEffect: HTTP Requests
Making HTTP requests are side effects! So we use useEffect()
Async/Await?
As we saw before, the only thing you can return in useEffect()
s is the cleanup function. This has implications if you were planning on doing async/await
.
When you make a function async, it automatically returns a promise. So you can't do:
// this does not work, don't do this:
React.useEffect(async () => {
const result = await doSomeAsyncThing()
// do something with the result
})
Instead you need to do:
React.useEffect(() => {
async function effect() {
const result = await doSomeAsyncThing()
// do something with the result
}
effect()
})
Kent prefers to use the .then
method as such:
React.useEffect(() => {
doSomeAsyncThing().then(result => {
// do something with the result
})
})
In this case, we're fetching Pokemon information based on an input name field.
Get Pokemon
The course provides a bunch of helper functions, so we're not going to get into the details of the HTTP request. Instead we'll focus on how to make the call, update our app, and handle errors.
Here's what our app looks like:
function App() {
const [pokemonName, setPokemonName] = React.useState('')
function handleSubmit(newPokemonName) {
setPokemonName(newPokemonName)
}
return (
<div className="pokemon-info-app">
<PokemonForm pokemonName={pokemonName} onSubmit={handleSubmit} />
<hr />
<div className="pokemon-info">
<PokemonInfo pokemonName={pokemonName} />
</div>
</div>
)
}
export default App
The components are a form with a button, and a box with Pokemon info. It looks like this:
The only thing we're playing with here is the PokemonInfo component, which looks like this:
function PokemonInfo({pokemonName}) {
const [state, setState] = React.useState({
pokemon: null,
error: null,
status: 'idle',
})
const {pokemon, error, status} = state
React.useEffect(() => {
if (!pokemonName) {
return
}
setState({status: 'pending'})
fetchPokemon(pokemonName).then(
pokemonData => {
setState({pokemon: pokemonData, status: 'resolved'})
},
error => {
setState({error: error, status: 'rejected'})
},
)
}, [pokemonName])
switch (status) {
case 'idle':
return 'Submit a pokemon'
case 'pending':
return <PokemonInfoFallback name={pokemonName} />
case 'resolved':
return <PokemonDataView pokemon={pokemon} />
case 'rejected':
throw error
default:
break
}
}
There are some interesting things going on here:
There is only one
state
object. This object is later updated usingsetState
.We maintain the current status of the app as a part of this state, and use the value of the status to decide later what to render.
The
switch
statement renders differently based on that status.
We also go into error handling, but I'll put that in a separate post!