I am working with Reactjs to pull information from a database and then display it in a pdf. I keep running into issues, and I think I have narrowed it down to where it is coming from, but not sure what is causing the issue.
In the following image you will note that I have a repeat of three console logs which is "list" and "provider".
The code for the main app is:
import Header from './components/top/header'
import List from './components/list'
import {GroceryDataProvider} from './context/DataContext'
function App() {
console.log('app')
return(
<div>
<h1>React Testing App</h1>
<GroceryDataProvider>
<Header/>
<List/>
</GroceryDataProvider>
</div>)
}
export default App;
the console.log "provider" is coming from my GroceryDataProvider. It is just a basic console log at the very top of it. Inside is one useEffect() and several functions. The following is the useEffect for DataProvider:
console.log('Provider')
const [groceryList, setGroceryList] = useState([]);
const [userList, setUserList] = useState([]);
const [userGroceryList, setUserGroceryList] = useState([]);
const [userItemList, setUserItemList] = useState([]);
const [userTypeList, setUserTypeList] = useState([]);
const [listId, setListId] = useState('');
const [userId, setUserId] = useState('');
const [DataLoad, setDataLoad] = useState(false);
//const [a, setA] = useState([]);
//const [lId, setLId] = useState(0);
useEffect(()=>{
console.log('effec')
const fetch = async()=>{
let db = firebase.database().ref('groceryA/');
db.once('value',snapshot=>{
snapshot.forEach(child=>{
setGroceryList(list=>[...list,child.val()])
})
})
db = firebase.database().ref('users/');
db.once('value',snapshot=>{
setUserList(snapshot.val());
})
db = firebase.database().ref('categories')
db.once('value',snapshot=>{
setUserTypeList(snapshot.val());
})
}
fetch();
},[])
//functions are here
const GetList = () => {
console.log('GETLIST');
setUserItemList([])
let db = firebase.database().ref('groceryA/'+listId+'/items');
db.once('value',snapshot=>{
snapshot.forEach(item=>{
setUserItemList(list=>[...list,item.val()])
})
})
}
return(
<DataContext.Provider value={{groceryList,userList,userGroceryList,userItemList,listId,userId,userTypeList,DataLoad,
setGroceryList,setUserList,setListId,setUserId,setUserTypeList,
ChangeUser,GetUserList,ChangeList,GetItemList,Updater,UpdateCart,WriteItem,WriteList,GetList,Delete}}>
{props.children}
</DataContext.Provider>
)
}
The other repeat console.log is "list" which again is just a basic console.log at the top of another component.
import React, {useContext,useEffect,useState} from 'react';
import { PDFDownloadLink, PDFViewer, Document, Page, Text } from '#react-pdf/renderer'
import MyDocument from './pdf';
import {DataContext} from 'context/DataContext'
import Card from './ItemCard';
const List = () =>{
console.log('list')
const {listId} = useContext(DataContext);
const {userItemList} = useContext(DataContext);
// {GetItemList} = useContext(DataContext);
const {Updater} = useContext(DataContext);
const {GetList} = useContext(DataContext);
const {Delete} = useContext(DataContext);
const {UpdateCart} = useContext(DataContext);
const [items, SetItems] = useState([]);
useEffect(()=>{
console.log('list id')
GetList();
},[listId])
useEffect(()=>{
console.log(userItemList)
console.log('item list')
SetItems([]);
SetItems(userItemList)
},[userItemList])
const productChange = (itemTxt, itemId) => {
SetItems(items.map(item=>{
if(item.listItemId===itemId){
return{...item,product:itemTxt}
}
return item;
}
))
}
const quantityChange = (itemTxt, itemId) => {
SetItems(items.map(item=>{
if(item.listItemId===itemId){
return{...item,quantity:itemTxt}
}
return item;
}
))
}
const cartChange = (itemId) => {
SetItems(items.map(item=>{
if(item.listItemId===itemId){
UpdateCart({...item,cart:!item.cart},item.listItemId)
return{...item,cart:!item.cart}
//console.log(item)
}
return item;}
//console.log(item)
}
))
}
return(
<div>
<p>To Find:</p>
<ul>
{//console.log(items)
}
{items.map((item,index)=>item.cart===false?
<Card key={item.listItemId} index={index}
value={item.listItemId} cart={item.cart} item={item.product}
units={item.quantity} unitType={item.unit}
cartChange={cartChange} itemChange={productChange} quantityChange={quantityChange} change={Updater} delete={Delete}/>:null)}
</ul>
<p>Found</p>
<ul>
{items.map((item,index)=>item.cart===true?<Card key={item.listItemId} index={index}
value={item.listItemId} cart={item.cart} item={item.product}
units={item.quantity} unitType={item.unit}
cartChange={cartChange} itemChange={productChange} quantityChange={quantityChange} change={Updater} delete={Delete}/>:null)}
</ul>
</div>
)
}
export default List;
What I don't understand is why only the context and list are repeating. I have another component called head which only renders once (third line down in the photo "head"). I thought it might have something to do with my useEffect inside the list component, but that is only called when there is a change, which does not happen at this point.
UPDATE
including Header component in response to alverio:
import {DataContext} from 'context/DataContext'
const UserSelect = () => {
const {userList} = useContext(DataContext);
const {ChangeUser} = useContext(DataContext);
return(
<div>
<select defaultValue={'default'} onChange={(e)=>ChangeUser(e.target.value)}>
<option value='default' disabled>Select a user</option>
{userList.map(user=>(
<option key={user.id} value={user.id}>{user.name}</option>
))}
</select>
</div>
)
}
const ListSelect = () => {
console.log('ListSelect')
const {GetUserList} = useContext(DataContext);
const {userGroceryList} = useContext(DataContext);
const {userId} = useContext(DataContext);
const {ChangeList} = useContext(DataContext);
useEffect(()=>{
GetUserList();
},[userId])
return (
<div>
<select defaultValue={'default'} onChange={(e)=>{ChangeList(e.target.value)}}>
<option value='default' disabled >Select a grocery list</option>
{userGroceryList.map(list=>(
<option key={list.listId} value={list.listId}>{list.title}</option>))}
</select>
</div>
)
}
const ListAdd = () => {
const {WriteItem} = useContext(DataContext);
const {WriteList} = useContext(DataContext);
const {userId} = useContext(DataContext);
const {userTypeList} = useContext(DataContext);
const [item, setItem] = useState('');
const [product, setProduct] = useState('');
const [quantity, setQuantity] = useState('');
const [category, setCategory] = useState('');
const [type, setType] = useState('');
let measureCategory = ['Each', 'Lb', 'Oz', 'Fl Oz']
let dataCategory = ['Other','Beverages', 'Bakery', 'Canned Goods', 'Dairy', 'Baking Goods', 'Frozen Foods', 'Meat', 'Produce', 'Snacks'];
console.log('ListAdd')
return (
<div>
<div>
<input id="list" type="text" value={item} onChange={(e)=>setItem(e.target.value)}/>
<button type="button" onClick={(e)=>{WriteList(item)}}>New List</button>
</div>
<div>
<input type="text" value={product} onChange={(e)=>setProduct(e.target.value)}/>
<select defaultValue={'default'} onChange={(e)=>{setCategory(e.target.value)}}>
<option value='default' disabled >Select a category</option>
{userTypeList.map(ul=>ul.id===parseInt(userId)?(ul.categories.map((uc,index)=><option key={index} value={uc}>{uc}</option>)):null)}</select>
<br/>
<input type="number" value={quantity} onChange={(e)=>setQuantity(e.target.value)}/>
<select defaultValue={'default'} onChange={(e)=>{setType(e.target.value)}}>
<option value='default' disabled >Select a unit</option>
{measureCategory.map((mc,index)=><option key={index} value={mc}>{mc}</option>)}</select>
<button type="button" onClick={(e)=>{WriteItem(product,category,quantity,type)}}>New Item</button>
</div>
</div>
)
}
const Header = () => {
console.log('head')
return (
<div>
<div>
<UserSelect/>
</div>
<div>
<ListSelect/>
</div>
<div>
<ListAdd/>
</div>
</div>
)
}
export default Header;
Related
I don't understand why my page can't recognize other pages when I click (for example on page 2, the same page appears again and again)
This is in MealNew.js component:
import React, {useEffect, useState } from "react";
import './MealNew.css';
import Card from "../UI/Card";
import AppPagination from "./AppPagination";
const MealNew = () => {
const [data, setData] = useState([]);
const [showData, setShowData] = useState(false);
const [query,setQuery] = useState('');
const[page,setPage] = useState(9);
const[numberOfPages,setNumberOfPages]= useState(10);
const handleClick = () => {
setShowData(true);
const link = `https://api.spoonacular.com/recipes/complexSearch?query=${query}&apiKey=991fbfc719c743a5896bebbd98dfe996&page=${page}`;
fetch (link)
.then ((response)=> response.json())
.then ((data) => {
setData(data.results)
setNumberOfPages(data.total_pages)
const elementFood = data?.map((meal,key) => {
return (<div key={key}>
<h1>{meal.title}</h1>
<img src={meal.image}
alt='e-meal'/>
</div> )
})
const handleSubmit = (e) => {
e.preventDefault();
handleClick();
}
useEffect(()=> {
handleClick();
},[page])
return (
<Card className="meal">
<form onSubmit={handleSubmit}>
<input
className="search"
placeholder="Search..."
value={query}
onChange={(e)=>setQuery(e.target.value)}/>
<input type='submit' value='Search'/>
</form>
<li className="meal">
<div className = 'meal-text'>
<h5>{showData && elementFood}</h5>
<AppPagination
setPage={setPage}
pageNumber={numberOfPages}
/>
</div>
</li>
</Card>
) }
export default MealNew;
This is in AppPagination.js component:
import React from "react";
import { Pagination } from "#mui/material";
const AppPagination = ({setPage,pageNumber}) => {
const handleChange = (page)=> {
setPage(page)
window.scroll(0,0)
console.log (page)
}
return (
<div >
<div >
<Pagination
onChange={(e)=>handleChange(e.target.textContent)}
variant="outlined"
count={pageNumber}/>
</div>
</div>
)
}
export default AppPagination;
Thanks in advance, I would appreciate it a lot
The only error I am getting in Console is this:
Line 64:3: React Hook useEffect has a missing dependency: 'handleClick'. Either include it or remove the dependency array react-hooks/exhaustive-deps
You are not following the spoonacular api.
Your link looks like this:
https://api.spoonacular.com/recipes/complexSearch?query=${query}&apiKey=<API_KEY>&page=${page}
I checked the spoonacular Search Recipes Api and there's no page parameter you can pass. You have to used number instead of page.
When you receive response from the api, it returns the following keys: offset, number, results and totalResults.
You are storing totalResults as totalNumberOfPages in state which is wrong. MUI Pagination count takes total number of pages not the total number of records. You can calculate the total number of pages by:
Math.ceil(totalRecords / recordsPerPage). Let say you want to display 10 records per page and you have total 105 records.
Total No. of Pages = Math.ceil(105/10)= 11
Also i pass page as prop to AppPagination component to make it as controlled component.
Follow the documentation:
Search Recipes
Pagination API
Complete Code
import { useEffect, useState } from "react";
import { Card, Pagination } from "#mui/material";
const RECORDS_PER_PAGE = 10;
const MealNew = () => {
const [data, setData] = useState([]);
const [showData, setShowData] = useState(false);
const [query, setQuery] = useState("");
const [page, setPage] = useState(1);
const [numberOfPages, setNumberOfPages] = useState();
const handleClick = () => {
setShowData(true);
const link = `https://api.spoonacular.com/recipes/complexSearch?query=${query}&apiKey=<API_KEY>&number=${page}`;
fetch(link)
.then((response) => response.json())
.then((data) => {
setData(data.results);
const totalPages = Math.ceil(data.totalResults / RECORDS_PER_PAGE);
setNumberOfPages(totalPages);
});
};
const elementFood = data?.map((meal, key) => {
return (
<div key={key}>
<h1>{meal.title}</h1>
<img src={meal.image} alt='e-meal' />
</div>
);
});
const handleSubmit = (e) => {
e.preventDefault();
handleClick();
};
useEffect(() => {
handleClick();
console.log("first");
}, [page]);
return (
<Card className='meal'>
<form onSubmit={handleSubmit}>
<input className='search' placeholder='Search...' value={query} onChange={(e) => setQuery(e.target.value)} />
<input type='submit' value='Search' />
</form>
<li className='meal'>
<div className='meal-text'>
<h5>{showData && elementFood}</h5>
<AppPagination setPage={setPage} pageNumber={numberOfPages} page={page} />
</div>
</li>
</Card>
);
};
const AppPagination = ({ setPage, pageNumber, page }) => {
const handleChange = (page) => {
setPage(page);
window.scroll(0, 0);
console.log(page);
};
console.log("numberOfPages", pageNumber);
return (
<div>
<div>
<Pagination
page={page}
onChange={(e) => handleChange(e.target.textContent)}
variant='outlined'
count={pageNumber}
/>
</div>
</div>
);
};
export default MealNew;
As the title states, I'm attempting to load images as soon as my react app loads.
I have a .jsx file, where there is a function called getHomePage().
The getHomePage() contains 4 functions.
When the getHomePage() function is called, it renders a dropdown. The dropdown contains an onChange event, where a user is able to select a collection of images. When this collection is selected, it calls a function called collectionChanged(e.target.value).
Goals:
What I want is for the images to load as soon as the application starts. Essentially, I want to call the collectionChanged(e.target.value) function when the application loads. So, I no longer want a user to select a collection, but for the collection of images to load as soon as the app loads.
How do I go about doing this? I hope all the above explanation made sense.
File: home.jsx
function getHomePage() {
const [token, setToken] = useState("");
const [visibility, setVisibility] = useState(false);
const [NFTBalances, setNFTBalances] = useState();
const [collection, setCollection] = useState();
const [nft, setNft] = useState();
const { Moralis } = useMoralis();
const handleChangeCollection = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
const topNFTs = query.limit(8);
const results = await topNFTs.find();
setNFTBalances(results);
};
}
const handleSelectToken = async (num, col) => {
if (num && col) {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
console.log(num);
query.equalTo("tokenId", num);
let selectedNFT = await query.first();
selectedNFT = selectedNFT.attributes;
console.log(selectedNFT);
setNft(selectedNFT);
setVisibility(true);
}
};
// FUNCTION I WANT TO CALL ONLOAD
const collectionChanged = async (col) => {
setCollection(col);
handleSelectToken(token, col);
handleChangeCollection(col);
};
const addToNFTs = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
query.limit(4);
const topNFTs = query.skip(NFTBalances.length);
const results = await topNFTs.find();
setNFTBalances(NFTBalances.concat(results));
}
return (
<>
// DROP DOWN SECTION
<div>
<select onChange={(e) => collectionChanged(e.target.value) }>
<option value="">Select a Collection</option>
<option value={"myCollection"}>My Collection</option>
</select>
</div>
<div className="row">
{NFTBalances && NFTBalances.map((nft, index) => {
return (
<div className="col-xxl-3 col-xl-3 col-lg-6 col-md-6">
<div className="card items">
<Card key={index} onClick={() =>
handleSelectToken(nft.attributes.tokenId,collection)}
cover={ <Image src={nft.attributes.image} /> }>
</Card>
</div>
</div>
);
})}
</div>
</>
);
}
export default getHomePage;
You should use the hook useEffect in order to load your images:
function getHomePage() {
const [token, setToken] = useState("");
const [visibility, setVisibility] = useState(false);
const [NFTBalances, setNFTBalances] = useState();
const [collection, setCollection] = useState();
const [nft, setNft] = useState();
const { Moralis } = useMoralis();
useEffect(() => {
//call your function to load your images
collectionChanged('myCollection')
}, [])
const handleChangeCollection = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
const topNFTs = query.limit(8);
const results = await topNFTs.find();
setNFTBalances(results);
};
}
const handleSelectToken = async (num, col) => {
if (num && col) {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
console.log(num);
query.equalTo("tokenId", num);
let selectedNFT = await query.first();
selectedNFT = selectedNFT.attributes;
console.log(selectedNFT);
setNft(selectedNFT);
setVisibility(true);
}
};
// FUNCTION I WANT TO CALL ONLOAD
const collectionChanged = async (col) => {
setCollection(col);
handleSelectToken(token, col);
handleChangeCollection(col);
};
const addToNFTs = async (col) => {
const dbNFTs = Moralis.Object.extend(col);
const query = new Moralis.Query(dbNFTs);
query.ascending("rank");
query.limit(4);
const topNFTs = query.skip(NFTBalances.length);
const results = await topNFTs.find();
setNFTBalances(NFTBalances.concat(results));
}
return (
<>
// DROP DOWN SECTION
<div>
<select onChange={(e) => collectionChanged(e.target.value) }>
<option value="">Select a Collection</option>
<option value={"myCollection"}>My Collection</option>
</select>
</div>
<div className="row">
{NFTBalances && NFTBalances.map((nft, index) => {
return (
<div className="col-xxl-3 col-xl-3 col-lg-6 col-md-6">
<div className="card items">
<Card key={index} onClick={() =>
handleSelectToken(nft.attributes.tokenId,collection)}
cover={ <Image src={nft.attributes.image} /> }>
</Card>
</div>
</div>
);
})}
</div>
</>
);
}
export default getHomePage;
What the code looks like rendering the button to show the form
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { deleteSong, getSongs, updateSong } from '../../store/song';
import ReactAudioPlayer from 'react-audio-player';
import { useHistory } from 'react-router';
import SongForm from '../AddSongForm';
import EditSongForm from '../EditSongForm';
const SongList = () => {
const [addShowForm, setAddShowForm] = useState(false);
const [editShowForm, setEditShowForm] = useState(false);
const history = useHistory()
const dispatch = useDispatch();
const songsObj = useSelector((state) => state.songState.entries);
const songs = Object.values(songsObj)
const user = useSelector((state) => state.session.user);
const CurrentUserId = user?.id
const remove = (e) => {
dispatch(deleteSong(e.target.id));
}
const addFormCheck = (e) => {
if (addShowForm) setAddShowForm(false)
if (!addShowForm) setAddShowForm(true)
}
const editFormCheck = (e) => {
if (editShowForm) setEditShowForm(false)
if (!editShowForm) setEditShowForm(true)
}
useEffect(() => {
dispatch(getSongs());
}, [dispatch]);
return (
<div>
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm ?
<SongForm />
: null}
</div>
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div className='songdetails' key={id}>
<p key={id}>songName={songName}</p>
<ReactAudioPlayer
src={songLink}
autoPlay
controls
key={songLink}
/>
{userId === CurrentUserId ?
<>
<div>
<button id={id} onClick={remove}>remove</button>
</div>
<div>
<button id={id} onClick={editFormCheck}>edit</button>
{editShowForm ?
<EditSongForm props={id} />
: null}
</div>
</>
: null}
</div>
))}
</ol>
</div>
);
};
export default SongList;
The actual form
import { useState } from "react";
import { useDispatch } from "react-redux";
import { updateSong } from "../../store/song";
import { useSelector } from "react-redux";
const EditSongForm = ({ props }) => {
console.log(props)
const dispatch = useDispatch();
const [songName, setSongName] = useState("");
const [songLink, setSongLink] = useState("");
const [errors, setErrors] = useState([]);
const reset = () => {
setSongName("");
setSongLink("");
// setAlbumName('');
// setArtistName('')
};
const user = useSelector((state) => state.session.user);
const userId = user?.id
const handleSubmit = async (e) => {
e.preventDefault();
const updatedSongDetails = {
id: props,
songName,
songLink,
userId
};
let updatedSong = await dispatch(updateSong(updatedSongDetails))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
reset();
};
return (
<div className="inputBox">
<h1>Update A Song</h1>
<ul>
{errors.map((error, idx) => <li className='errors' key={idx}>{error}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setSongName(e.target.value)}
value={songName}
placeholder="Song Name"
name="Song Name"
/>
<input
type="text"
onChange={(e) => setSongLink(e.target.value)}
value={songLink}
placeholder="Song Link"
name="Audio File"
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default EditSongForm;
Right now when I have a list of songs and click the button for the edit form to appear it applies to the entire list if I have more than one song uploaded. I'm not sure how to make it specific enough so only one form opens at a time.
The solution was to create a component for specific song details and then render that in the .map.
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div>
<SpecificSong id={id} songName={songName} songLink={songLink} userId={userId} />
</div>
))}
</ol>
So I was trying to update the value I got by the Addlist and I tried this but this isn;t working. Also when I click on the '+' button without writing anything, an empty list is created. How should I stop it. I've attached a code below.
import React from "react";
import "./App.css";
import { useState } from "react";
import TodoList from "./components/TodoList";
function App() {
const [input, setInput] = useState("");
const [list, setList] = useState([]);
const updateList = (e) => {
setInput(e.target.value);
};
const AddList = () => {
console.log("value added")
setList((addValue) => {
return [...addValue, input];
});
setInput("");
};
const updateItems=(id)=>{
const newValue=[...list].map((newVal)=>{
if(input.id===id){
input.text='';
}
return newVal;
})
setList(newValue);
}
const deleteItems = (id) => {
console.log("deleted");
setList((addValue) => {
return addValue.filter((element, index) => {
return index !== id;
});
});
};
return (
<div className="todo-app">
<h1> Enter Anything</h1>
<input
type="text"
placeholder="Add anything"
value={input}
onChange={updateList}
/>
<button onClick={AddList}>+</button>
<ul>
{list.map((itemsvalue, id) => {
return (
<TodoList
itemsValue={itemsvalue}
key={id}
onSelect={deleteItems}
id={id}
onUpdate={updateItems}
/>
);
})}
</ul>
</div>
);
}
export default App;
Any kind of help would be appreciated. Also if I want to split this into multiple components is there a way to do.
When user clicks on the add button there is the check for empty String AddList method
for ex:- User updates second index value, second position value will get updated.
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const [index, setIndex] = useState(null);
const updateList = (e) => {
setInput(e.target.value);
};
useEffect(() => {
setList(list);
console.log(list, '<>?');
}, [index]);
const AddList = () => {
if (input.trim() !== '') {
setList([...list, input]);
}
setInput('');
};
const updateValue = (index) => {
console.log(list[index]);
setIndex(index);
if (list[index].trim() !== '') {
setInput(list[index]);
}
};
const UpdateList = () => {
list[index] = input;
console.log(list, 'before <>?');
setIndex(null);
setInput('');
};
return (
<div>
<input type="text" placeholder="Add anything" value={input} onChange={updateList} />
<button disabled={!index && !list.length === 0} onClick={AddList}>
Add
</button>
<button disabled={input.trim() === ''} onClick={UpdateList}>
Update
</button>
{list.map((m, index) => (
<h1 style={{ border: '1px solid black' }} onClick={() => updateValue(index)}>
{m}
</h1>
))}
</div>
);
As the query will fetch the value that is provided in useState. But I want the search bar to search for recipes provided by me. Can anybody help me on how I can do that.
import React, { useEffect, useState } from 'react';
import Recipe from './Recipe';
import './App.css';
const App = ()=>{
const APP_ID= '2*****'
const APP_KEY= 'f******************'
const [recipes, setRecipes] = useState([]);
const [search, setSearch] = useState("");
const [query, setQuery] = useState('chicken');
useEffect(() =>{
const getRecipes = async()=>{
const response = await fetch(`https://api.edamam.com/search?q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`);
const data = await response.json();
setRecipes(data.hits)
};
getRecipes();
},[query]);
const updateSearch = e=>{
setSearch(e.target.value);
}
const getSearch = e =>{
setQuery(search);
setSearch('');
}
return(
<div className="App">
<form onSubmit={getSearch} className="search-form">
<input className="search-bar" type="text" value={search} onChange={updateSearch} />
<button className="search-button" type="submit">Search</button>
</form>
{recipes.map(recipe =>(
<Recipe
key={recipe.recipe.label}
title={recipe.recipe.label}
calories={recipe.recipe.calories}
image={recipe.recipe.image} />
))}
</div>
)
}
export default App;
don't create multiple states for the same thing.
pass search string to the fetch API
import React, { useState, useEffect } from "react";
import "./styles.css";
const App = () => {
const APP_ID = "2***********";
const APP_KEY = "f*********";
const [recipes, setRecipes] = useState([]);
const [search, setSearch] = useState("");
useEffect(() => {
try {
const getRecipes = async () => {
const response = await fetch(
`https://api.edamam.com/search?q=${search}&app_id=${APP_ID}&app_key=${APP_KEY}`
);
const data = await response.json();
setRecipes(data.hits);
};
getRecipes();
} catch (error) {
// handle error here
console.error(error);
}
}, [search]);
const updateSearch = (e) => {
setSearch(e.target.value);
};
const getSearch = (e) => {
setSearch("");
};
return (
<div className="App">
<form onSubmit={getSearch} className="search-form">
<input
className="search-bar"
type="text"
value={search}
onChange={updateSearch}
/>
<button className="search-button" type="submit">
Search
</button>
</form>
{recipes.map((recipe) => (
<Recipe
key={recipe.recipe.label}
title={recipe.recipe.label}
calories={recipe.recipe.calories}
image={recipe.recipe.image}
/>
))}
</div>
);
};
export default App;
edit:
If initially, you want to have chicken results from API, change the response variable to this:
const response = await fetch(
`https://api.edamam.com/search?q=${search || "chicken"}&app_id=${APP_ID}&app_key=${APP_KEY}`
);