I'm very new to JavaScript and the world of React. I've learning hooks, and tried to fetch some API for searchbar and it doesn't work.
I'm trying to grab the data from the url (its array) and search bar to filter items by its title.
function Search() {
const [searchTerm, setSearchTerm] = useState([]);
const [text,setText] = useState([]);
const getAPI = async() => {
const response = await fetch("https://fakestoreapi.com/products")
const data = await response.json()
setText(data.Search)
}
useEffect( () => {
getAPI()
}, [])
return <div>
<input
placeholder="searching"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}/>
</div>
};
there is not any key with Search name inside response data you should setState all your data and then filter with input text value.
Please read useEffect documentation.
useEffect( () => {
getAPI()
}, [])
The above code only runs when the component is mounted. When the user changes the value of input, Search component rerenders, because its state changes. But useEffect will not be executed because you have provided an empty array of dependencies.
You have declared getAPI inside your component. So you should probably add it to the array of dependencies of useEffect.
You should call getAPI in onChange of the input. So it fetches the data from server based on the query parameters.
You have not used searchTerm inside getAPI function.
Be aware of infinite loop caused by useEffect.
Related
i have another "movie database" application in react. At the mount it renders movies based on api key which is set to "new movies". Then i have useEffect which update movie list based on searchbar and its value. Problem is, it renders new movies and just after that renders movies based on searchbar value which is empty. I know that useEffect is running on mount. What is best practice to use it this way? Or is there any better hook for this particular use? Thank you.
React.useEffect(() => {
fetch(
`https://api.themoviedb.org/3/search/movie?api_keylanguage=en-US&query=${searchValue}&`
)
.then((res) => res.json())
.then((data) => {
setMovies(data.results);
});
}, [searchValue]);
Hey #JSpadawan best practice is to use filter like-
const [data,setData] = useState()
useEffect(()=>{
const apiData = fetch(APILINK).then((res)=>res.json()).catch(err)
if(apiData.length>0){
setData(apiData)
}
},[])
This will set the data of api.. now use filter like-
const [searchValue, setSearchValue]= useState()
const filterData = data && data.filter((data)=>data.includes(searchValue))
After this you can use filterdata any where you want. If you still facing issue just lemme know, i will help you more.
Thanks
You can set the movies with the useEffect, but I would recommend having a secondary useEffect that would be for the seach bar. You should try to avoid making tons of API calls as the more you make, the more it will slow down the application. So have one useEffect that will set the data, and a second one that you can filter out the ones that match the search bar.
If the searchValue is updated every key stroke then searching for a movie with a long title would create tons of API calls. You could put the fetch call in an async/await function so you get the data, then set it, then you can filter with a different effect.
useEffect(() => {
const getData = async () => {
await fetch(
`https://api.themoviedb.org/3/search/movie?api_keylanguage=en-US&query=${searchValue}&`
)
.then((res) => res.json())
.then((data) => {
setMovies(data.results);
});
}
getData();
}, []); // empty array will run once on component load, or add a dependency of when you want it to re-call the api
Then a second useEffect for filtering
useEffect(() => {
let searchTextFilter = movies.filter(i => {
return (!searchValue || (searchValue && i.Title.toLowerCase().includes(searchFilter?.toLowerCase() )))
});
}, [searchValue])
So I recently started to discover ReactJS. I have a simple Spring Boot api which has a few methods. One of these returns a list of objects. I get these in my frontend by using Axios to make the HTTP call.
export function getItems() {
const [items, setItems] = useState([]);
useEffect(async () => {
await client.get('items').then((result) => {
setItems(result.data);
});
}, []);
return items;
The items are mapped in a gallery component and shown on screen. Next to this, I have an addItem function which posts an item object obtained through a form to my api.
export async function addPet(newPet) {
await client.post(
'/pets',
newPet,
);
}
My AddPet component is loaded inside my Gallery component. The form is shown on the right side of the screen and when I click the "Add Item" button, I want the item to be added and my list of items reloaded to show the newly added item. Right now, I can not get this working in a correct way. If I remove the "[]" from the final part of my useEffect() in my getItems() functions, everything seems to work but in reality the app is making the getItems call over and over again. If I add "[]", the call is only made once at the start, but will not re-render the gallery component when an item is added. The handleSubmit() for my "Add item" button is as follows:
const handleSubmit = () => {
const newItem = new Item();
newItem .name = formValue.name;
newItem .image = formValue.image;
newItem .pitemText = formValue.itemText;
addItem(newItem);
};
So my question here is: how can I get that gallery component to re-render whenever I add a new item or delete one of the items? I figure I need a way to change the state of the component but I can't seem to get it done.
The second parameter of useEffect (the Array) has an important role: the items in that array trigger the useEffect to re-run.
Some cases:
useEffect(() => {}, []): runs once, after the component is mounted
useEffect(() => {}, [var1, var2,..., varn]): runs when var1 or var2 or varn is updated
useEffect(() => {}): runs on every completed re-render (default behavior)
More on useEffect: useEffect hook
So, your code works as expected:
useEffect(() => {
client.get('items').then((result) => {
setItems(result.data);
});
}, []); // -> runs once, when component is mounted
useEffect(() => {
client.get('items').then((result) => {
setItems(result.data);
});
}, [item]); // -> runs when the variable named item changes
you need to organize your code in such a way, that this useEffect hook can run on the update of the variable whose change you want to watch.
dont pust async as the first parameter of useEffect hook as below, wont work well
useEffect(async () => {
await client.get('items').then((result) => {
setItems(result.data);
});
}, []);
instead you can use external function or IIEF function as below
useEffect(() => {
(async () => {
await client.get('items').then((result) => {
setItems(result.data);
});
})()
}, []);
I am trying to access the useState variable query in my function inside useEffect. I get the error React Hook useEffect has a missing dependency: 'query'. Either include it or remove the dependency array.
I think the problem is that im setting the (useState) query variable when the user types in the search bar and then I am trying to access this new query variable in the useEffect hook.
I want to fetch the api after I join the api url and the contents of query but after setQuery is executed which is after the user types in the search bar. How can I do this?
Thanks
Heres the code; notice the query variable.
import React, { useState, useEffect } from "react";
import Grid from '#mui/material/Grid';
import PaperCard from '../components/ResearchPaperCard';
const apiUrl = "http://127.0.0.1:8000/api/search/?search=";
function SearchedPapers(){
const [query, setQuery] = useState("");
const [apiData, setApiData] = useState([])
useEffect(() => {
const getFilteredItems = async (query) => {
let response = await fetch(apiUrl+query);
let papers = await response.json();
setApiData(papers);
if (!query) {
return papers
}
return papers;
}
getFilteredItems(query);
},[]);
console.log(apiData)
return (
<div className='SearchedPapers'>
<label>
Search
</label>
<input type='text' onChange={e => setQuery(e.target.value)}/>
<div>
{apiData.map((paper) => {
return (
<Grid key={paper.title}>
<PaperCard title={paper.title} abstract={paper.abstract}/>
</Grid>
)
})}
</div>
</div>
)
}
export default SearchedPapers;
Right now your useEffect is triggered each time your component mount so basically when someone reaches this screen.
At this time your query state is empty.
Try adding this :
useEffect(() => {
const getFilteredItems = async (query) => {
let response = await fetch(apiUrl+query);
let papers = await response.json();
setApiData(papers);
if (!query) {
return papers
}
return papers;
}
getFilteredItems(query);
},[query]) - - - - > here
By doing so, your useEffect will be triggered only and each time your query state changes
You should either include query in your dependency array i.e., second argument of your UseEffect hook or either remove. When it's(array) empty useEffect will only render once i.e., when your page render for first time but when removed useEffect runs both after the first render and after every update. When specified like you put 'query' in dependency array it will only run whenever there is change in 'query' state.
My component relies on local state (useState), but the initial value should come from an http response.
Can I pass an async function to set the initial state? How can I set the initial state from the response?
This is my code
const fcads = () => {
let good;
Axios.get(`/admin/getallads`).then((res) => {
good = res.data.map((item) => item._id);
});
return good;
};
const [allads, setAllads] = useState(() => fcads());
But when I try console.log(allads) I got result undefined.
If you use a function as an argument for useState it has to be synchronous.
The code your example shows is asynchronous - it uses a promise that sets the value only after the request is completed
You are trying to load data when a component is rendered for the first time - this is a very common use case and there are many libraries that handle it, like these popular choices: https://www.npmjs.com/package/react-async-hook and https://www.npmjs.com/package/#react-hook/async. They would not only set the data to display, but provide you a flag to use and show a loader or display an error if such has happened
This is basically how you would set initial state when you have to set it asynchronously
const [allads, setAllads] = useState([]);
const [loading, setLoading] = useState(false);
React.useEffect(() => {
// Show a loading animation/message while loading
setLoading(true);
// Invoke async request
Axios.get(`/admin/getallads`).then((res) => {
const ads = res.data.map((item) => item._id);
// Set some items after a successful response
setAllAds(ads):
})
.catch(e => alert(`Getting data failed: ${e.message}`))
.finally(() => setLoading(false))
// No variable dependencies means this would run only once after the first render
}, []);
Think of the initial value of useState as something raw that you can set immediately. You know you would be display handling a list (array) of items, then the initial value should be an empty array. useState only accept a function to cover a bit more expensive cases that would otherwise get evaluated on each render pass. Like reading from local/session storage
const [allads, setAllads] = useState(() => {
const asText = localStorage.getItem('myStoredList');
const ads = asText ? JSON.parse(asText) : [];
return ads;
});
You can use the custom hook to include a callback function for useState with use-state-with-callback npm package.
npm install use-state-with-callback
For your case:
import React from "react";
import Axios from "axios";
import useStateWithCallback from "use-state-with-callback";
export default function App() {
const [allads, setAllads] = useStateWithCallback([], (allads) => {
let good;
Axios.get("https://fakestoreapi.com/products").then((res) => {
good = res.data.map((item) => item.id);
console.log(good);
setAllads(good);
});
});
return (
<div className="App">
<h1> {allads} </h1>
</div>
);
}
Demo & Code: https://codesandbox.io/s/distracted-torvalds-s5c8c?file=/src/App.js
I have a API.js file that I'm using to return an api call. I need to return the artistID from the first API call to retrieve the data from the second API call. The issue is, I need to retrieve data from both of the API's in order to display/render the data on my SearchResults component.
Use async/await and call the second API call from inside the callback on the first one.
I have also used a useEffect with a dependency of the props from the first API call so my second request fires any time a hook is updated. Useful if you are pulling a list with an API call, grab the URI to another API call from the body of the first one. Hope this helps!
Edit: OP asked for some code so here goes. In this example I have the fetchData function grabbing data with Axios, it can either get a call from the useEffect in EventList or the useEffect in CardBody. EventList grabs all the events from my GitHub. Inside EventList I have a child component of CardBody that has a prop of the url field from the events API end point. Since the dependency of the useEffect inside CardBody is set to [props] that will run every time the prop is changed. I use this method because CardBody is inside a events.map which is the data I get back from the events endpoint.
const fetchData = async (query, uri) => {
await axios.get(uri).then(res => {
const dataFromServer = res.data;
query(dataFromServer)
})
};
const CardBody = props => {
const [repo, setRepo] = useState([]);
useEffect(() => {
fetchData(setRepo, props.eventRepo)
}, [props]);
return (
<div>
Repo: <a href={repo.html_url}>{repo.name}</a>
</div>
);
};
const EventList = () => {
const [event, setEvent] = useState([]);
useEffect(() => {
fetchData(setEvent, "https://api.github.com/users/foestauf/events")
}, []);
return (
<CardBody eventRepo={id.repo.url}/>
)
}