Howcome my state is not updating using react hooks and use Effect - reactjs

My useEffect function is trying to fetch data from an API endpoint. The results resultAllTestTypes are currently logging fine.
However, I can't find out why the allTestTypes are coming back as undefined as I thought I had already set it in a state variable it should be able to log it to the console. But when I log the allTestTypes data it gives me this.
Code:
const [allTestTypes, setAllTestTypes] = useState([])
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
console.log('resultAllTestTypes data ',resultsAllTestTypes.data);
console.log('allTestTypes data ',allTestTypes.data);
}
onLoadCreateUnitTests()

It's setting the state, you just have a console.log in a spot that's not particularly useful.
allTestTypes is a local const. It will never change, and that's not what setAllTestTypes is trying to do. When you set state, this tells react to render the component again. When that render occurs, you'll make a new call to useState, which will return the new value and assign it to a new local const. That new variable can be interacted with by code in the new render, but code from the previous render (such as your console.log) will never see the new value.
If you'd like to verify that the component is rerendering with a new value, move your console.log into the body of the component:
const [allTestTypes, setAllTestTypes] = useState([])
console.log('Rendering with', allTestTypes);
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
}
onLoadCreateUnitTests()
});

cuz setAllTestTypes is async, so u can't get it immediately.
if u want to use it ,use the local variable resultsAllTestTypes instead

Related

usestate not setting the data

i am using usestate for transfer data. but ufotunately it not quite work.
here is my code:
const [totCons, settotCons] = useState(null)
useEffect(() => {
// declare the async data fetching function
const fetchData = async () => {
// get the data from the api
const data = await fetch('https://piscons2.vercel.app/ConsPiscTotCons');
// convert the data to json
const json = await data.json();
// set state with the result
settotCons(json);
console.log(json)
console.log(totCons)
}
// call the function
fetchData()
// make sure to catch any error
.catch(console.error);;
}, [])
as you can see on image the json return data but the totCons return null.
i did set it settotCons(json)
Updated state will not be available to the state value immedieately.
The react setState is asynchronous, but thats not the only reason for this behaviour. The reason is a closure scope around an immutable const value.
Both props and state are assumed to be unchanging during 1 render.
Treat this.state as if it were immutable.
You can use useEffect to create the sideeffects for totCons
useEffect(() => {
// action on update of totCons
}, [totCons]);
try doing console.log(totCons) outside useEffect.
you will not get the updated value in next line.
you will get the updated value in next render

RTK Query response state

I'm trying to convert some Axio code to RTK query and having some trouble. The 'data' response from RTK query doesn't seem to act like useState as I thought.
Original axio code:
const [ importantData, setImportantData ] = useState('');
useEffect(() => {
async function axiosCallToFetchData() {
const response = await axiosAPI.post('/endpoint', { payload });
const { importantData } = await response.data;
setImportantData(importantData);
}
axiosCallToFetchData()
.then((res) => res)
.catch((error) => console.log(error));
}, []);
const objectThatNeedsData.data = importantData;
New RTK Query code
const { data, isSuccess } = useGetImportantDataQuery({ payload });
if(isSuccess){
setImportantData(data.importantData);
}
const objectThatNeedsData.data = importantData;
This however is giving me an infinite render loop. Also if I try to treat the 'data' object as a state object and just throw it into my component as:
const objectThatNeedsData.data = data.importantData;
Then I get an undefined error because it's trying to load the importantData before it's completed. I feel like this should be a simple fix but I'm getting stuck. I've gone through the docs but most examples just use the if statement block to check the status. The API calls are being made atleast with RTK and getting proper responses. Any advice?
Your first problem is that you always call setImportantData during render, without checking if it is necessary - and that will always cause a rerender. If you want to do that you need to check if it is even necessary:
if(isSuccess && importantData != data.importantData){
setImportantData(data.importantData);
}
But as you noticed, that is actually not necessary - there is hardly ever any need to copy stuff into local state when you already have access to it in your component.
But if accessing data.importantData, you need to check if data is there in the first place - you forgot to check for isSuccess here.
if (isSuccess) {
objectThatNeedsData.data = data.importantData;
}
All that said, if objectThatNeedsData is not a new local variable that you are declaring during this render, you probably should not just modify that during the render in general.

My code doesn't fetch data until after a re-render

So i have been trying to fetch data from an API and the link was dependent on a url parameter, but the data is fetched only after a re-render and i cant access them in my render function.
It works on normal pages but when i try to fetch data inside a route depending on a parameter passed with that route, it doesnt work. How could i solve this problem?
NOTE: my fetch is inside a different component with a different file from the one im passing the parameter with
const { name } = useParams();
const [countries, setCountries] = useState([]);
const fetchData = async () => {
const { data } = await axios.get(`https://restcountries.eu/rest/v2/name/${name}`);
setCountries(data);
};
useEffect(() => {
fetchData();
}, []);
Its normal behavior as you and sending an async call to fetch data, and at that time 1st render is called,
To handle this you should add a new state called loading and set it true before calling API and set it false once you got data,
and on bases on loading state, show some loader in ur render method
First of all, you should set dependencies array in your useEffect, so for now, you've set it as empty [], and it means that it will execute only the first render. So, as I understood, you wanna execute ur fetchData function every 'name' params update, so you need to add that param to ur useEffect function, this way:
useEffect(() => {
fetchData();
}, [name]);
Hope I've understood ur request correctly.
I was trying access the fetched data before specifying the index and I didn't use map.

Update array using useState

I'm calling an API inside the useEffect hook and trying to update my state covidData but my array remains empty even after calling the setData function:
const [covidData, setData] = useState([]);
useEffect(() => {
async function getGlobalData() {
let response = await fetch('https://api.covid19api.com/summary');
let jsonResponse = await response.json();
const globalData = jsonResponse.Global;
//setting covidData
setData([...covidData, {recovered: globalData.TotalRecovered}])
console.log(covidData); //covidData is empty
}
getGlobalData()
}, [])
what am I doing wrong?
Your code is correct, the state is updated asynchronously, it's normal your console.log will not display your state after your setState.
It is how react works, when you change state of something, react creates new instance of virtual dom.
So when you change state of covidData the value you set will be in new instance and the console.log is still in that old instance so it logs old value which is empty as you set when using useState.
Try logging with button onClick event and you will see your data or you can check with React Dev Tools
Also you can refactor your code as
useEffect(async () => {
let response = await fetch('https://api.covid19api.com/summary');
let jsonResponse = await response.json();
const globalData = jsonResponse.Global;
//setting covidData
setData([...covidData, {recovered: globalData.TotalRecovered}])
}, [])
More on virtual dom
React Docs
What is virtual DOM

Setting state after data load on useEffect

Attempting to update state after data load on useEffect. Able to update a local variable but not state. I am following an example from https://www.robinwieruch.de/react-hooks-fetch-data/ where Robin sets state in a similar way. For some reason, the state variable is never set correctly in my case.
Using AWS Amplify to load graphQL data. Seems to work successfully for local variable but not state variable.
const [serviceTypes, setServiceTypes] = useState([{}]);
let myServiceTypes = [{}]; // try it with a local variable to debug
useEffect(() => {
let unmounted = false;
async function fetchData() {
const result = await API.graphql(
graphqlOperation(queries.listServiceTypes)
);
console.log(result);
console.log('setting service types...');
console.log(result.data.listServiceTypes.items);
setServiceTypes(result.data.listServiceTypes.items);
myServiceTypes = result.data.listServiceTypes.items;
console.log(myServiceTypes); // set correctly
console.log(serviceTypes); // empty
}
if (!unmounted) {
fetchData();
}
return () => {
unmounted = true;
};
}, []);
Expecting serviceTypes to be set to the data loaded. Instead it is empty.
setState does not work synchronously like that. You cannot expect your serviceTypes variable to immediately contain the data right after you set it. It will be updated when the component re-renders. try moving the console.log(serviceTypes); outside of the useEffect.
see https://stackoverflow.com/a/36087156/5273790 for an explanation of setState async.
It's because you aren't thinking about the effect and renders correctly.
Mounting/Render 1
fetchData is called
setServiceTypes and passes the service type to the next render
serviceTypes is empty
Render 2
useEffect is not called
serviceTypes is now what the previous serviceTypes was set to
Try logging out the serviceTypes right before the render/return

Resources