Reputation: 15
I am learning React and working on a simple "search for recipe" app using an API. I have created a search form component that captures the value in a useState hook then passing the "text" value to the app using "onAdd" prop. The text value should update the url after being passed into a second useState [query]. When I type a term into the form then click the search button, nothing happens. When I click the search button again, the data shows up on the page.
I understand this has something to do with the useState hook being asynchronous and the value not being available until the next render. How can I change this code so the url updates and the data is displayed on the page with a single click?
My SearchForm component:
import { useState } from 'react';
import Card from "./Card";
import Button from "./shared/Button";
function SearchForm({ onAdd }) {
const [text, setText] = useState('');
const [btnDisabled, setBtnDisabled] = useState(true);
const [message, setMessage] = useState('');
const handleTextChange = (e) => {
if (text === '') {
setBtnDisabled(true)
setMessage(null)
} else if (text !== '' && text.trim().length <= 2) {
setBtnDisabled(true)
setMessage('Text must be at least 4 characters')
} else {
setMessage(null)
setBtnDisabled(false)
}
setText(e.target.value)
}
function submitRecipe(event) {
event.preventDefault();
onAdd(text)
setText('');
}
return (
<form onSubmit={submitRecipe}>
<Card>
<h2>Find Your Perfect Dish</h2>
<div className='input-group'>
<input onChange={handleTextChange} type='text' placeholder="Search for a Recipe" value={text} />
<Button type='submit' version='secondary' isDisabled={btnDisabled} >Search</Button>
</div>
{message && <div className="message">{message}</div>}
</Card>
</form>
)
}
export default SearchForm
My App component:
import { useState } from 'react';
import Header from "./components/Header";
import SearchForm from "./components/SearchForm";
import {v4 as uuidv4} from 'uuid'
import Axios from 'axios'
import Recipe from './components/Recipe';
function App() {
const [query, setQuery] = useState('');
const [recipes, setRecipes] = useState([]);
const APP_ID =
const APP_KEY =
const url = `https://api.edamam.com/search?q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`
const getData = async (text) => {
setQuery(text)
const result = await Axios.get(url);
setRecipes(result.data.hits);
}
return (
<>
<Header />
<div className="container">
<SearchForm onAdd={getData} />
{recipes !== [] && recipes.map( recipe =>
<Recipe key={uuidv4} recipe={recipe}/>
)}
</div>
</>
);
}
export default App;
Upvotes: 0
Views: 534
Reputation: 364
The problem is that you are updating the state, and then performing the call immediately, but the state is not updated before on the next render.
Try this in stead:
const getData = async (text) => {
const url = `https://api.edamam.com/search?q=${text}&app_id=${APP_ID}&app_key=${APP_KEY}`
const result = await Axios.get(url);
setRecipes(result.data.hits);
}
Upvotes: 3