Why fetched data do not populate the state after i set them? - reactjs

Data fetched successfully but my state does not change.
I initialize the state categories as an empty [] then I want to set it to the data I fetched. Response is OK and I can see the data in the console.
It is an object with one pair: categories:Array(14) which is what I am looking for.
const [categories,setCategories] = useState([])
useEffect(() => {
const fetchMeals = async () => {
const res = await fetch(
"https://www.themealdb.com/api/json/v1/1/categories.php"
);
const data = await res.json();
console.log(data.categories); /*array (14)*/
setCategories(data.categories.map((cat) => cat.strCategory));
console.log(categories); /*empty array*/
};
fetchMeals();
},[]);
Why doesn't work?

setState is async like operation so you would never see updated state synchronously (after invoking setState).
You can do alternatively:
const updatedCategiories = data.categories.map((cat) => cat.strCategory);
setCategories(updatedCategiories);
console.log(updatedCategiories);

Related

useEffect is causing infinite loop when use state as dependency

Here simply I am fetching data from mysql DB and storing it in state and in order to fetch this data:
const [orders, setOrders] = useState([]);
To fetch data I am using different functions and finally I am calling those functions using useEffect simple enough and so for everything is working perfectly but the problem comes whenever I use the state as dependency where I am storing data beacause if I dont do that then I have to manually refresh the page for latest changes and I have tried every given solution on stackoverflow but any of the solution didnt work so someone can please help me how can I use this state as dependencey without causing infinite loop:
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
loadData();
loadTotal();
}, [orders]);
console.log(orders);
If you move the state into the useEffect dependency, you can then check if it is empty, and only set it when that check passes.
It will set the state once to populate and not pass the check again.
const [orders, setOrders] = useState([]);
const loadData = async () => {
const response = await fetch("http://localhost/k-shop/load.php");
const result = await response.json();
setOrders(result);
};
const loadTotal = async () => {
const response = await fetch("http://localhost/k-shop/amount.php");
const result = await response.json();
setTotal(result);
};
useEffect(() => {
if(orders.length === 0) {
loadData();
}
// you can do the same with checking loadTotal() state
}, [orders]);
console.log(orders);
Avoid ,non-primitive data types in dependencyArray ,
useEffect(() => {
loadTotal();
loadData();
}, [total, orders.length]);
every times you "setOrders" means you change the state,every times you change the state,means the "useEffect" will do again.that cause infinite loops.why not try useEffect(() => {loadData()}, [])?

state data still keeps the previous state even after setState

when I fetch data from my api successfully, I can display it using console.log, but somehow after I use a setState, the state still keeps the previous data. so when the page loads, the console.log(data) has an array of object containing data from the api, but console.log(rute) after the setRute still returns empty array. why is that?
this is my code
const MasterRute = () => {
const [rute, setRute] = useState([]);
const getRute = async () => {
const response = await fetch('http://localhost:8080/rute');
const data = await response.json();
// console.log(data); // [{id:"1", name:"AAA"}, {id: "2", name: "BBB"}]
setRute(data);
// console.log(rute); // [] ===> why is this empty?
}
}
any help is appreciated
useState is asynchronous, so you won't see changes after setRute. You need to wait for the component re-rendered completely
const MasterRute = () => {
const [rute, setRute] = useState([]);
const getRute = async () => {
const response = await fetch('http://localhost:8080/rute');
const data = await response.json();
// console.log(data); // [{id:"1", name:"AAA"}, {id: "2", name: "BBB"}]
setRute(data);
}
console.log(rute); //data updated on rendering
}
You can check this article for a better understanding
If you still really want to see data after setRute. You can get the result within setTimeout
const MasterRute = () => {
const [rute, setRute] = useState([]);
const getRute = async () => {
const response = await fetch('http://localhost:8080/rute');
const data = await response.json();
// console.log(data); // [{id:"1", name:"AAA"}, {id: "2", name: "BBB"}]
setRute(data);
setTimeout(() => {
console.log(rute); //data updated
})
}
}

change in state does not re-renders the component

const [list, setList] = useState([]);
useEffect(() => {
const getData = async () => {
let storeList = [];
for (let i = 0; i<50; i++) {
try {
const token = await contract.methods.tokenOfOwnerByIndex(accountId , i).call();
let data = await contract.methods.tokenURI(token).call();
const receivedData = await fetch(data);
let jsonData = await receivedData.json();
storeList.push(jsonData);
setList(storeList);
} catch(error) {
cogoToast.error(`Error caused in fetching data of ${i} number token`);
}
}
};
if (contract) getData();
}, [contract]);
the initial state of "list" is empty. When the loop is run for 1st time and the list is updated for the first time, the component re-renders, but after that, even though the state is changing the component does not re-render.
Ideally, the component should re-render when the state changes.
You're mutating the array that already exists, not creating a new one. When you set state in a function component, react does a === between the old state and the new state. If they're the same, it skips rendering.
To fix this, create a new array and set state with that:
let jsonData = await receivedData.json();
setList(prev => [...prev, jsonData]);

How to save data from axios.get to hooks right away

How will I able to save in hooks that data from database. Since I need to display the data that I get to dropdown.
Here's my code
const [dataSystem, setdataSystem] = useState([])
const getAllSystems = async() => {
......
}
const getDependentSystems = async() => {
const response = await axios.get('/API' + ID)
console.log('LIST OF SYSTEM', response.data)
setdataSystem(response.data)
}
Since upon setState, data is not yet saved to dataSystem I need to trigger getDeoendetSystems() twice to display the list on my dropdown.
Result of console.log
LIST OF SYSTEM [{...},{...}]
0: {ID: 1, SYSTEMID: 12 ...},
1: {ID: 2, SYSTEMID: 13 ...}
Thank you
You need to load the data inside an useEffect like
function Component() {
const [dataSystem, setdataSystem] = useState([])
useEffect(() => {
getDependentSystems()
}, [])
const getDependentSystems = async() => {
const response = await axios.get('/API' + ID)
console.log('LIST OF SYSTEM', response.data)
setdataSystem(response.data)
}
return ...
}
Basically you want to call the function in the useEffect so you only call it once; cause if you call it in the component context, everytime the state updates it will call the api again which will trigger an infinite loop.

async fetch triggered three times

I use this in my react app to fetch data from my backend server:
React.useEffect(() => {
const fetchWidgets = async () => {
const response = await fetch("http://localhost:1202/data");
const responseData = await response.json();
setData(responseData);
console.log(responseData);
};
fetchWidgets();
});
It fetching data works fine, but the function seems to be triggered three times for some reason.
responseData is logged three times.
React.useEffect runs every time after component renders, unless you tell it not by defining a dependency array as its second argument; since you are setting a state inside its body which causes the comonent to re-render, you will see it happens multiple times. to fix the problem you may pass an empty array [] it will only run once after first render and acts like componentDidMount in class components. or add some dependency to run only if the dependencies change;
React.useEffect(() => {
const fetchWidgets = async () => {
const response = await fetch("http://localhost:1202/data");
const responseData = await response.json();
setData(responseData);
console.log(responseData);
};
fetchWidgets();
},[]);
Use Empty Brackets for the second parameter of useEffect.
React.useEffect(() => {
const fetchWidgets = async () => {
const response = await fetch("http://localhost:1202/data");
const responseData = await response.json();
setData(responseData);
console.log(responseData);
};
fetchWidgets();
},[]);
That will ensure the useEffect only runs once.

Resources