I am using useState to render the list of recipes based on the API call. i want to pass the setRecipeList function created in RecipeList component in the App.js component to render the list of recipes based on the response
RecipeList.js
import React, { useState } from "react";
import Recipe from "./Recipe";
import styles from "./RecipeList.module.css";
const RecipeList = (props) => {
const [recipeList, setRecipeList] = useState([]);
setRecipeList();
return (
<div className={styles.recipe_list}>
{recipeList.length &&
recipeList.map((recipeObj) => (<Recipe recipeObj={recipeObj} />))}
</div>
);
};
export default RecipeList;
App.js
import React, { useState } from "react";
import Axios from "axios";
import Header from "./Components/Header";
import RecipeList from "./Components/RecipeList";
const APP_ID = "a6fee00c";
const APP_KEY = "a32eaaa328924a4d92edf81b006740e8";
function App() {
const [timeoutId, setTimeoutId] = useState();
const fetchRecipe = async (searchString) => {
const response = await Axios.get(
`https://api.edamam.com/search?q=${searchString}&app_id=${APP_ID}&app_key=${APP_KEY}`
);
console.log(response);
};
const onTextChangeHandler = (e) => {
//debouncing - to skip API calls for initial letters when entering the input
clearTimeout(timeoutId);
const timeout = setTimeout(() => fetchRecipe(e.target.value), 500);
setTimeoutId(timeout);
};
return (
<>
<Header onTextChange = {onTextChangeHandler} />
<RecipeList/>
</ >
);
}
export default App;
link for the complete code - https://github.com/Moulya27/Recipe-Finder
CodePudding user response:
if you want a simple solution with useState, then I'd recommend to shift the useState to the App.js and pass the setRecipeList down into the RecipeList.js component.
RecipeList.js
...
const RecipeList = (props) => {
const { recipeList, setRecipeList } = props;
...
App.js
...
function App() {
const [timeoutId, setTimeoutId] = useState();
const [recipeList, setRecipeList] = useState([]);
...
return (
<>
<Header onTextChange = {onTextChangeHandler} />
<RecipeList recipeList={recipeList} setRecipeList={setRecipeList} />
</ >
);
}
export default App;
I didn't get much what you do with the setRecipeList in the recipe component but definitely it is good to wrap the fetch data into useEffectHook. Good luck!:)
CodePudding user response:
In order to pass data downstream, i.e. from App
to RecipeList
, you need to use props. Rather than declaring recipeList
and setRecipeList
in RecipeList
, you could instead declare them in App
, use setRecipeList
inside fetchRecipe
, and pass recipeList
as a prop to RecipeList
, where it can consume the data as it changes. Something like the following:
function App() {
...
const [recipeList, setRecipeList] = useState([]);
const fetchRecipe = async (searchString) => {
const response = await Axios.get(
`https://api.edamam.com/search?q=${searchString}&app_id=${APP_ID}&app_key=${APP_KEY}`
);
setRecipeList(response);
};
...
return (
<>
<Header onTextChange={onTextChangeHandler} />
<RecipeList { ...recipeList } />
</>
);
}
const RecipeList = ({recipeList}) => {
return (
<div className={styles.recipe_list}>
{recipeList.length &&
recipeList.map((recipeObj) => (<Recipe recipeObj={recipeObj} />))}
</div>
);
};
Notice how I've used spread syntax to feed recipeList
to RecipeList
and destructuring assignment to pull recipeList
directly out of RecipeList
's props
. I could also have said <RecipeList recipeList={recipeList} />
and (props) => ... props.recipeList ...
.
In general, the idiom in react is to create state at higher levels and pass either the state variable (recipeList
), the setter function (setRecipeList
), or both down to lower levels to suit your purpose. This can get unwieldy, however. Other options include useReducer and React Redux, which allow more fine-grained control.
Finally, as Yoshi also mentions, anything with timers should be dealt with using useEffect. Here's a previous answer of mine that demonstrates how and why.