after axios get my setState hooks doesn't run - reactjs

I have a lot of functions like this, using the same approach, and they all run fine.
But this one is going me crazy.
My code is:
const [match,setMatch] = useState(null);
axios.get(path)
.then((res) => {
status = res.data;
setMatch(res.data)});
const test = match;
If I debug the program and break at setMatch(status), the content of res.data is correct:
res.data is an array, and each element contains a mix of values and objects as show in the screenshot below.
But when I execute the setMatch(status) the result is that the match state variable doesn't change: it remains null as set the first time.
Can someone provide some suggestion?

From your question, I dont know what could likely affect your code from running. But this is the common pattern when handling asynchronous request.
It is expected that it should be executed in the useEffect hook right after it is painted
const Component = () => {
const [match,setMatch] = useState([]);
useEffect(() => {
axios.get(path)
.then((res) => {
status = res.data;
setMatch([match, ...res.data])})
.catch((err) => handleError(err));
}, [path]) // assuming that the path changes otherwise if you are running this once, leave the array empty
return (<div> {(match.length > 0)? 'Loading': {renderYourLogicHere} </div>)
}

Related

react error: Empty array after setting an const

please why do i get an empty array when i do console.log(customers) but i get the right response which is status 200 when i do console.log(response.data);. i dont know if i am doing setCustomers(response.data.customers); in a wrong way. please help me
const [customers, setCustomers] = useState([]);
useEffect(() => {
const storedToken = JSON.parse(localStorage.getItem("token"))
const fetchCustomers = async () => {
const config = {
headers: { Authorization: `Bearer ${storedToken}` }
};
const response = await axios.get('https://exampleofalink.com/api/v1/customer', config);
console.log(response.data); // status 200
setCustomers(response.data.customers);
console.log(customers); //empty
};
fetchCustomers();
}, []);
i also tried
setCustomers(prevCustomers => [...prevCustomers, ...response.data.customers]);
but i still get an empty array
don't try to console log when react is still settting the values, you will not see the change, but the values are there!
u can place another different useEffect to see the change like this:
useEffect(() => {
console.log(customers);
}, [customers]);
When you setting a state, it doesn't immediately change the value, it will changed on re-render, so you will see the previous value which is an empty array and can't see the newest change immediately. There are two option if you want to see the values:
Put console.log outside the useEffect
Or, add console.log into another useEffect that run when customers value changed
useEffect(() => {console.log(customers)}, [customers])
See also:
The useState set method is not reflecting a change immediately

usestate can change the state value after axios in useEffect

I expected to get the url with category=business,but the web automatically reset my state to the url that dosent have the category.I dont know the reason behind
let {id}=useParams()
const [newsurl,setNewsurl]=useState(()=>{
const initialstate="https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
return initialstate;})
//console.log(id);
const [articles, setActicles] = useState([]);
useEffect( ()=>{
if(id === 2)
console.log("condition")
setNewsurl("https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee")},[])
useEffect(() => {
const getArticles = async () => {
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state ha
s been updated.
}, [newsurl])
//return "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";}
return (<><Newsnavbar />{articles?.map(({title,description,url,urlToImage,publishedAt,source})=>(
<NewsItem
title={title}
desciption={description}
url={url}
urlToImage={urlToImage}
publishedAt={publishedAt}
source={source.name} />
)) } </>
)
one more things is that when i save the code the page will change to have category but when i refresh it ,it change back to the inital state.Same case when typing the url with no id.May i know how to fix this and the reason behind?
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
You can instead, for example, use a useEffect hook that is dependant on the relevant state in-order to see that the state value actually gets updates as anticipated.
Example:
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state has been updated.
}, [newsurl])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "newsurl" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
setState is an async operation so in the first render both your useEffetcs run when your url is equal to the default value you pass to the useState hook. in the next render your url is changed but the second useEffect is not running anymore because you passed an empty array as it's dependency so it runs just once.
you can rewrite your code like the snippet below to solve the problem.
const [articles, setActicles] = useState([]);
const Id = props.id;
useEffect(() => {
const getArticles = async () => {
const newsurl =
Id === 2
? "https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
: "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);

React setState inside useEffect async

I am attempting to perform a series of Axios requests inside the useEffect() of a react component. I am aware that these requests are asynchronous, and I should maintain a piece of "loading" state that specifies if series of requests have been completed.
const [state, updateState] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
let innerstate = []
allRespData.map(single_response => {
axios.post("<URL>", {
raw_narrative: single_response[index].response
})
.then((response) => {
innerstate.push(response.data)
});
})
updateState(innerstate)
setLoading(false)
}, []);
if (loading)
return (<h3> Loading </h3>)
else {
console.log(state)
return (<h3> Done </h3>)
}
I would expect the output from the above code to be a list containing the data of each response. Unfortunately, I think that data only arrives midway through the console.log() statement, as initially an empty list [] is logged, however the list is expandable- therein my expected content is visible.
I am having a hard time doing anything with my state at the top, because the list length is constantly 0, even if the response has already loaded (loading == false).
How can I assert that state has been updated? I assume the problem is that the loading variable only ensures that a call to the updateState() has been made, and does not ensure that the state has actually been updated immediately thereafter. How can I ensure that my state contains a list of response data so that I can continue doing operations on the response data, for example, state.forEach().
You're not awaiting any of the requests, so updateState will get called before any of the responses have had time to come back. You'll be setting the state as [] every time. You also need to return your axios.post or the data won't get passed to .then
There are lot of nicer ways to handle this (I'd recommend looking at the react-query library, for example). However, to make this work as it is, you could just use Promise.all(). Something like:
useEffect(() => {
Promise.all(
allRespData.map(single_response =>
axios
.post('<URL>', { raw_narrative: single_response[index].response })
.then(response => response.data)
.catch(error => {
// A single error occurred
console.error(error);
// you can throw the error here if you want Promise.all to fail (or just remove this catch)
})
)
)
// `then` will only be called when all promises are resolved
.then(responses => updateState(responses))
// add a `.catch` if you want to handle errors
.finally(() => setLoading(false));
}, []);

ReactJS array from usestate is still empty after setTimeout

please I'm solving one problem (just learning purposes). I'm using useState() hook and then, after some timeout I want add next items into array from remote fetch.
My code snippet look like:
const [tasks, setTasks] = useState([]);
const url = 'https://pokeapi.co/api/v2/pokemon?offset=0&limit=5';
// asnynchronous call. Yes, it's possible to use axios as well
const fetchData = async () => {
try {
let tasksArray = [];
await fetch(url)
.then((response) => response.json())
.then((data) => {
data.results.map((task, index) => {
// first letter uppercase
const taskName = task.name.charAt(0).toUpperCase() + task.name.slice(1);
tasksArray.push({ id: index, name: taskName });
});
});
console.log('Added tasks:' + tasks.length);
setTasks(_.isEmpty(tasks) ? tasksArray : [...tasks, tasksArray]);
} catch (error) {
console.log('error', error);
}
};
useEffect(() => {
// Add additional example tasks from api after 5 seconds with
// fetch fetchData promise
setTimeout(fetchData, 5000);
}, []);
Code works fine with useEffect() hook. But in async function my array is empty when I add some tasks within five seconds and it will be replaced by fetched data and one empty
I added Butter and Milk within 5 seconds to my Shopping list
But after timeout my tasks array will be replaced by remote fetched data.
And as you can see, tasks array lenght is 0 (like console.log() says)
Please, can you exmplain me, why my tasks array is empty if there exists 2 items before 5 seconds.
Of course, I'm adding my tasks to the list normally after hit Enter and handleSubmit
const handleSubmit = (e) => {
e.preventDefault();
//creating new task
setTasks([
{
id: [...tasks].length === 0 ? 0 : Math.max(...tasks.map((task) => task.id)) + 1,
name: newTask,
isFinished: false,
},
...tasks,
]);
setNewTask('');
}
Thanks for help or explain. It the problem that useEffect is called after rendering? Of this causing async behavior?
I could not understand your code fully correctly but my guess is
the fetchData function you have declared might refer to the tasks at the time of declaration.
so every time you call fetchData you might not see the changed tasks state...
if this is the case try using useCallback with the dependency tasks...
what useCallback does is it stores the function in memory and if smth in dependency changes the function's logic changes to dependencies you declared.
If you have used eslint, calling a function inside useEffect will give you error similar to below
The ‘fetchOrg’ function makes the dependencies of useEffect Hook (at line 6) change on every render. Move it inside the useEffect callback. Alternatively, wrap the ‘fetchOrg’ definition into its own useCallback() Hook
Your code is confusing. You can place everything inside an useEffect, and I believe the thing you are trying to achieve is a long poll query. (for that you use setInterval() and you have to clear the function in useEffect
my solution for you:
const [tasks, setTasks] = useState([]);
const url = 'https://pokeapi.co/api/v2/pokemon?offset=0&limit=5';
const request = {method: GET, Headers: {"Content-type":"application/json"}}
useEffect(() => {
function fetchData(){
fetch(url, request).then(res => res.json().then(data => {
data.results.map((task, index) => {
const taskName = task.name.charAt(0).toUpperCase() + task.name.slice(1);
setTasks((prevState) => ([...prevState,{ id: index, name: taskName }]));
});
})
}
const interval = setInterval(() => {
fetchData();
}, 5000)
return () => clearInterval(interval)
}, []);
please do not forget two things:
This approach is only good when you have 5 to 10 simultaneous clients connected since it is not performance effective on the backend, I would recommend instead an alternative based on realtime communication (with the listening for new events.
please do not forget to specify the {method, headers, body) in the fetch function

Problem occurs when use find function in React

I am new to react and I try to get data from the backend and view that data in the frontend. To do that I tried this code:
function VProfile() {
const buyerId=(localStorage.getItem("userId"));
console.log(buyerId);
const [buyerDetails, setBuyerDetails] = useState({});
useEffect(() => {
axios
.get(`/getBuyerDetails`)
.then((response) => setBuyerDetails(response.data.existingBuyers))
.catch((err) => console.error(err));
}, []);
console.log(buyerDetails);
const oneBuyer = buyerDetails?.find(oneBuyer => oneBuyer.buyerId === buyerId);
console.log(oneBuyer);
}
When I call the API I get a length 3 array of objects. This is an image of the data.
Then I try to find the data of a specific buyer using the find function. To do that I use this const oneBuyer = buyerDetails?.find(oneBuyer => oneBuyer.buyerId === buyerId) code. But then I got an error that says TypeError: buyerDetails.find is not a function. How do I silve this problem?
Thank You!
You initialize the state with an object ({}):
const [buyerDetails, setBuyerDetails] = useState({});
Objects don't have .find, only arrays do.
You might want to initialize it to an empty array ([]), or undefined to more clearly signal the case that the data is not yet loaded.
You call setBuyerDetails asynchronously, so at the moment when you log them, the data hasn't arrived, due to console.log you are being "deceived" to believe the data is there. From docs:
Please be warned that if you log objects in the latest versions of
Chrome and Firefox what you get logged on the console is a reference
to the object, which is not necessarily the 'value' of the object at
the moment in time you call console.log(), but it is the value of the
object at the moment you open the console.
To avoid that error you can initialize that variable with empty array initially:
const [buyerDetails, setBuyerDetails] = useState([]);
Do it something like that and initialize with an array and you are using async operations so please add the loading flag
function VProfile() {
const buyerId=(localStorage.getItem("userId"));
console.log(buyerId);
const [buyerDetails, setBuyerDetails] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
axios
.get(`/getBuyerDetails`)
.then((response) => {
setBuyerDetails(response.data.existingBuyers));
setLoading(false);
})
.catch((err) => console.error(err));
}, []);
console.log(buyerDetails);
if (loading) return <p>loading....</p>
const oneBuyer = buyerDetails?.find(oneBuyer => oneBuyer.buyerId === buyerId);
console.log(oneBuyer);
}

Resources