API based search using React - javascript

I am new to React and I want to run API based search. I have written a sample code with the search functionality but it is not working as per requirement. I want to search from the list but it is always giving the same array of as the whole list when I am writing anything in the search box.
Please help me out and let me know where the code in wrong.
Here is my code:
TestEntry.js
import React , {useState,useEffect} from 'react'
import {Table} from 'reactstrap'
import {Navbar,Nav,NavDropdown,Form,FormControl,Button} from 'react-bootstrap'
//import axios from 'axios'
import Loading from './loading.gif';
const CoinGecko = require('coingecko-api');
const CoinGeckoClient = new CoinGecko();
function TestEntry(){
const[item,SearchData]=useState([]);
const[cryptos,setCryptos]=useState([]);
useEffect(()=>{
fetchItems()
},[])
const fetchItems=async()=>{
const url="https://api.coingecko.com/api/v3/coins/list";
const response= await fetch(url);
const info=await response.json();
console.log(info);
setCryptos(info);
}
const Search=(key)=>{
console.log(key);
fetch("https://api.coingecko.com/api/v3/coins/list?q="+key)
.then((data)=>{
data.json().then((resp)=>{
console.warn("resp:",resp)
SearchData(resp)
})
})
}
const cryptoJsx=cryptos.map(crypto=>(
<div key={crypto.id}>
{crypto.id}
</div>
));
return(
<div>
Search:
<input type="text" onChange={(event)=>Search(event.target.value)}/>
<div>
{
{item} ?
<div>
{
item.map((items)=>
<div key={items.id}>{items.name}</div>
)
}
</div>
: ""
}
</div>
{cryptoJsx}
</div>
)
}
export default TestEntry

The search api seems not working. When I tried api-search for a text separately in browser, it returned full results.
Anyway....
You can do search locally i.e. filter the cryptos array. It will cause re-render and only filtered results are shown.
Note:
Maintain a copy of the cryptos and always filter based on original and only mutate the cryptos. This way search works (both typing a char and deleting a char) and search results are re-rendered automatically
The downside of filtering state data is that new data from server is only obtained in client upon page refresh
If you really want to use api docs and use correct endpoint. Also consider using debouncing. Perform debounce in React.js
I have checked this and search is working fine.
import React, { useState, useEffect } from "react";
function TestEntry() {
const [item, SearchData] = useState([]);
const [cryptos, setCryptos] = useState([]);
const [origCryptosCount, setOrigCryptosCount] = useState([]);
useEffect(() => {
fetchItems();
}, []);
const fetchItems = async () => {
const url = "https://api.coingecko.com/api/v3/coins/list";
const response = await fetch(url);
const info = await response.json();
setCryptos(info);
setOrigCryptosCount(info);
};
// const Search_Old = key => {
// console.log(key);
// fetch("https://api.coingecko.com/api/v3/coins/list?q=" + key).then(data => {
// data.json().then(resp => {
// SearchData(resp);
// });
// });
// };
//
const Search = key => {
const newResults = origCryptosCount.filter(crypto => crypto.name.includes(key));
console.log('newResults', newResults);
setCryptos(newResults);
};
const cryptoJsx = cryptos.map(crypto => (
<div key={crypto.id}>{crypto.id}</div>
));
return (
<div>
Search:
<input type="text" onChange={event => Search(event.target.value)} />
{cryptoJsx}
</div>
);
}
export default TestEntry;

Related

How to pass the data input from one component into another component?

Introducing The Problem
I am beginner ReactJS learner developing a simple weather app using OpenWeather API. The app is designed to fetch data from two components: one that returns the current weather of the user input and another one that returns the weather forecast for the next 5 days.
When the city name is typed down into the input field, the following message appears on the console:
GET https://api.openweathermap.org/data/2.5/weather?q=undefined&units=metric&appid=${Api.key} 400 (Bad Request)
I do not know how to pass the data from Search Component into App Component. Seriously, I have tried a lot of alternatives but they have been unsuccessful. There are commented lines of code to show my last try so far.
(ignore ForecastWeather because this component is empty)
I know that all of you are quite busy folks, but I would appreciate the help in a respectful way. Even suggestions about what I have to study (e.g. callBack) are welcome. I've tried this already:
https://stackoverflow.com/questions/56943427/whether-to-save-form-input-to-state-in-onchange-or-onsubmit-in-react
https://sebhastian.com/react-onchange/
The code is forward below:
App.js
import React, { useState } from "react";
import { Api } from "./Api";
import {
Search,
CurrentWeather,
ForecastWeather,
Footer,
} from "./components/index";
import "./App.css";
function App() {
const [getCity, setGetCity] = useState();
const [weatherData, setWeatherData] = useState(null);
const [forecastData, setForecastData] = useState(null);
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
return (
<div className="App">
<Search
searchResultData={handleSearchLocation}
textPlaceholder="Search for a place..."
/>
{weatherData && <CurrentWeather resultData={weatherData} />}
<ForecastWeather resultData={forecastData} />
<Footer />
</div>
);
}
export default App;
Search.jsx
import React, { useState } from "react";
function Search({ textPlaceholder, searchResultData }) {
const [searchCity, setSearchCity] = useState("");
//const handlerOnChange = ( event, dataSearch ) => {
//setSearchCity(event.target.value);
//setSearchCity(dataSearch);
//searchResultData(dataSearch);
//};
return (
<div className="componentsBoxLayout">
<input
value={searchCity}
//onChange={handlerOnChange}
onChange={(event) => setSearchCity(event.target.value)}
onKeyDown={(event) => event.key === "Enter" && searchResultData(event)}
placeholder={textPlaceholder}
/>
</div>
);
}
export default Search;
CurrentWeather.jsx
import React from "react";
function CurrentWeather({ resultData }) {
return (
<div className="componentsBoxLayout">
<p>{resultData.name}</p>
</div>
);
}
export default CurrentWeather;
ForecastWeather.jsx (empty)
import React from 'react';
function ForecastWeather() {
return (
<div className="componentsBoxLayout">ForecastWeather</div>
)
}
export default ForecastWeather;
Api.js
const Api = {
url: "https://api.openweathermap.org/data/2.5",
key: "etcetc",
img: "https://openweathermap.org/img/wn",
};
export { Api };
Yippee-ki-yay
You can not use getCity in this function:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${getCity}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${getCity}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setGetCity(dataSearch);
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
getCity is defined on that function so it does not exist when you try to use it, unless you need getCity later for another component I would delete it becuase is redundant and do this:
const handleSearchLocation = (dataSearch) => {
const weatherDataFetch = fetch(
`${Api.url}/weather?q=${dataSearch}&units=metric&appid=${Api.key}`
);
const forecastDataFetch = fetch(
`${Api.url}/forecast?q=${dataSearch}&units=metric&appid=${Api.key}`
);
Promise.all([weatherDataFetch, forecastDataFetch])
.then(async (response) => {
const weatherResponse = await response[0].json();
const forecastResponse = await response[1].json();
setWeatherData(weatherResponse);
setForecastData(forecastResponse);
})
.catch(console.log);
};
When you run searchResultData on the search component you send the city you are looking for. Remember that useState will trigger a re-render but a function that is already running before that will never get the new value of the state if the state changes

React useState array empty on initial load but after editing code while app is running array fills?

This is going to be really hard to explain, but here goes. I am building a React card grid with a filter. The data is pulled from an MySQL AWS API I built. The .tags property is JSON with an array that stores each tag associated with the card. I have written Javascript in App.jsx to turn this JSON into an object, and then store every unique tag in a piece of state. See code below:
//App.jsx
import { useEffect, useState } from 'react';
import '../assets/css/App.css';
import Card from './Card';
import Filter from './Filter'
import {motion, AnimatePresence} from 'framer-motion'
function App() {
const [cards, setCards] = useState([]);
const [filter, setFilter] = useState([]);
const [activeFilter, setActiveFilter] = useState("all");
const [tags,setTags] = useState([]);
useEffect(() => {
fetchData();
}, []);
/*useEffect(() => {
console.log(tags);
console.log(activeFilter);
}, [activeFilter,tags]);
*/
const getTags = () => {
let tags = [];
cards.forEach((card) => {
let obj = JSON.parse(card.tags);
obj.forEach((tag) => {
if (!tags.includes(tag)) {
tags.push(tag);
}
});
});
setTags(tags);
}
const fetchData = async () => {
const data = await fetch("<<api>>");
const cards = await data.json();
setCards(cards);
setFilter((cards));
getTags();
}
return (
<div className="App">
<Filter
cards={cards}
setFilter={setFilter}
activeFilter={activeFilter}
setActiveFilter={setActiveFilter}
/>
<motion.div layout className="Cards">
<AnimatePresence>
{filter.map((card) => {
return <Card key={card.id} card={card}/>;
})}
</AnimatePresence>
</motion.div>
</div>
);
}
export default App;
The problem that I am having is that when I run the app initially, the tags state is empty when inspecting from React Dev tools. However, when I keep the app running, and then add something like a console.log(tags); before setTags(tags) is called in the getTags() function, the data suddenly appears in the state. If someone could explain why the state seems to be empty even though I am updating it on the initial render that would be really appreciated.
You are running getTags on empty array. setCards doesn't set the const variable instantly. New values will be present in the next render cycle.
Try adding cards param
const getTags = (cards) => {
let tags = [];
cards.forEach((card) => {
let obj = JSON.parse(card.tags);
obj.forEach((tag) => {
if (!tags.includes(tag)) {
tags.push(tag);
}
});
});
setTags(tags);
}
And use it like this:
const fetchData = async () => {
const data = await fetch("API url");
const cards = await data.json();
setCards(cards);
setFilter((cards));
getTags(cards);
}

How do I access a numeric key in firebase realtime database?

I'm quite new to Reactjs and Firebase so I'd need help.
I need to retrieve the data from the "0","1","2" keys (i.e. "Price", "Title", etc.) like I do with the "user" one.
In this parent component (Dashboard.js) I connect to the database:
import { useState, useEffect } from "react";
import OrderedItems from "./OrderedItems";
import classes from "./Dashboard.module.css";
import dash from '../../assets/dash.jpg';
import { auth } from "../../firebase";
import { useHistory } from "react-router-dom";
const Dashboard = () => {
const [ord, setOrd] = useState([]);
const history = useHistory();
useEffect(() => {
const fetchLps = async () => {
const query = `${auth.currentUser.uid}/Orders.json`
const response = await fetch(
"https://beatles-app-default-rtdb.europe-west1.firebasedatabase.app/" + query
);
const responseData = await response.json();
console.log(responseData);
const loadedOrd = [];
for (const key in responseData) {
loadedOrd.push({
id: key,
orderedItems: responseData[key].orderedItems,
});
}
setOrd(loadedOrd);
// setIsLoading(false);
};
fetchLps().catch((err) => {
let errorMessage =
"Something went wrong on our server! Please try again later.";
alert(errorMessage);
history.replace("/");
});
}, [history]);
const OrdList = ord.map((things) => (
<OrderedItems
key={things.id}
id={things.id}
orderedItems={things.orderedItems}
/>
));
return (
<section className={classes.cont}>
<div className={classes.welcome}>
<h1 className={classes.h1}>Welcome to your Dashboard!</h1>
<h3 className={classes.h3}> You can now start to shop. Go to the Shop page, select your favorite albums and go to Cart
in order to submit your order.</h3><br/>
</div>
{OrdList}
<img src={dash} alt="" width="100%" />
</section>
);
};
export default Dashboard;
console.log(responseData) output:
I retrieve the data in this child component (OrderedItems.js) using this syntax {props.user.address} (for example in this case I get "street" which is correct):
const OrderedItems = (props) => {
return (
<section>
<div>
<p>{props.user.address}</p> // THIS WORKS
<p>{props.orderedItems.0.Price}</p> // THIS DOESN'T WORK BECAUSE OF THAT 0 WHICH IS THE NAME OF THE FIREBASE KEY
</div>
</section>
);
};
export default OrderedItems;
Now the problem is that I can't do it with the above mentioned keys as the following syntax is not allowed because of that 0 number {props.orderedItems.0.Price}.
What should I do? The database structure is created automatically as I'm entering orders of multiple items using the POST method. So I suppose that the solution would be either to use a proper syntax (which I don't know at the moment) in order to access the data or to populate the database in a different way (but I don't think it is possible as Firebase does it on its own).
Thank you in advance!

react constantly calling the API when using .map to go through the list

i have setup a strapi API, and i am using react to consume that API (with Axios).
here's what the code look like inside App.js
import axios from "axios";
import React, {useEffect, useState} from "react";
import LineCard from "./components/Linecard"
function App() {
// don't mind the URL i will fix them later
const root = "http://localhost:1337"
const URL = 'http://localhost:1337/pick-up-lines'
// this is the "messed up" data from strapi
const [APIdata, setAPIdata] = useState([])
//this is the clean data
const [lines, setLines] = useState([])
// the array that i will be using later to "setLines" state
const linesFromApi = APIdata.map((line, index) => {
const profileImage = root + line.users_permissions_user.profilePicture.formats.thumbnail.url
const userName = line.users_permissions_user.username
const title = line.title
const lineBody = line.line
const rating = line.rating
const categories = line.categories.map((category, index) => category.categoryName)
return {
profileImage,
userName,
title,
lineBody,
rating,
categories
}
})
useEffect(() => {
// calling the API with get method to fetch the data and store it inside APIdata state
axios.get(URL).then((res) => {
setAPIdata(res.data)
})
setLines(linesFromApi)
}, [URL, linesFromApi])
return (
<div>
// mapping through the lines list and rendering a card for each element
{lines.map((line, index) => <LineCard line={line} />)}
</div >
);
}
export default App;
i know for sure that this is causing the problem
return (
<div>
{lines.map((line, index) => <LineCard line={line} />)}
</div >
);
my problem is that react keeps sending GET requests constantly, and i want it to stop after the first time it has the list.
how can i do that!
Try adding a check in your hook so that it restricts the api call if the value is already set.
Something like this
useEffect(() => {
if(lines.length === 0){
axios.get(URL).then((res) => {
setAPIdata(res.data)
})
setLines(linesFromApi)
}
}, [URL, linesFromApi])
You need to add the key property to the element in a map.
<div>
{lines.map((line, index) => <LineCard key={index} line={line} />)}
</div>

logging the data but not rendering p tag , why?

I am using firebase firestore and i fetched the data , everything is working fine but when i am passing it to some component only one item gets passed but log shows all the elements correctly.
I have just started learning react , any help is appreciated.
import React, { useEffect, useState } from 'react'
import { auth, provider, db } from './firebase';
import DataCard from './DataCard'
function Explore() {
const [equipmentList, setEquipments] = useState([]);
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
data.docs.forEach(item => {
setEquipments([...equipmentList, item.data()]);
})
}
useEffect(() => {
fetchData();
}, [])
equipmentList.forEach(item => {
//console.log(item.description);
})
const dataJSX =
<>
{
equipmentList.map(eq => (
<div key={eq.uid}>
{console.log(eq.equipment)}
<p>{eq.equipment}</p>
</div>
))
}
</>
return (
<>
{dataJSX}
</>
)
}
export default Explore
You have problems with setting fetched data into the state.
You need to call setEquipments once when data is prepared because you always erase it with an initial array plus an item from forEach.
The right code for setting equipment is
const fetchData = async () => {
const res = db.collection('Available');
const data = await res.get();
setEquipments(data.docs.map(item => item.data()))
}

Categories

Resources