Wrong value of state displayed after refreshing a page from useEffect() - reactjs

I have a problem with my page that fetch data from a server and displays it for the user. I am using the hook useEffect for it.
My problem is that the first time I visit a page the correct data are displayed (so from /home I go to /product1 I get correct information). But after manually refreshing my page /product1 even though the server is getting the correct information again, my state object of my page will not update again. Is there a way to fix this? Code below:
const [productInfo, setProductInfo] = useState({
saleStarted : null,
quantityPerSize : []
})
useEffect(() => {
const fetchdata = async () => {
setLoading(true);
const query = new db.Query('Products');
query.equalTo('productId', params.productId);
await query.find()
.then(async (response) => {
setdata(response[0].attributes);
})
.catch((err) => {
setError(err);
console.log(err);
})
.finally(() => {
setLoading(false);
});
}
if(isInitialized){
fetchdata();
}
}, [isInitialized]);
useEffect(() => {
const saleStartedInfo = async() => {
const options = {
link : url + params.contractId,
}
try{
let started = await db.find(options)
console.log(started); //returns true
setProductInfo({...productInfo, saleStarted : started});
}catch(e){
console.log(e);
}
}
const quantitySize = async() => {
let _quantityBySize = [];
for(let k = 0 ;k<data.numberOfSize;k++) {
let options = {
address : url + params.contractId,
}
try{
let quantitySize = await db.find(options);
_quantityBySize.push(quantitySize)
} catch(err) {
console.log(err);
}
}
console.log(_quantityBySize); // let's say returns [5,4,10] if product has 3 size
setContractInfo({...contractInfo, quantityPerSize : _quantityBySize})
}
if(isInitialized && data){
saleStartedInfo();
quantityMinted();
}
}, [data])
So after rendering this page the first time it will show sale started, and quantity 5, 4 and 10. After refresh it will show sale not started and quantity === [] (empty array)

Related

Fetching an array of objects from POKEAPI using REACT.js and AXIOS {Answered}

I chose to start learning API handling with POKEAPI. I am at a step where I need to get the flavor_text of each pokemon (the description let's say) but I can't for some reason.
Here is the JSON structure for one specific pokemon: https://pokeapi.co/api/v2/pokemon-species/bulbasaur.
And here is my useEffect trying to get it. The line fetching the habitat works and displays on my website so I guess my issue comes from my map in setDescription but I can't be sure.
export default function Card({ pokemon }, { key }) {
const src = url + `${pokemon.id}` + ".png";
const [habitat, setHabitat] = useState(null);
const [descriptions, setDescriptions] = useState([]);
useEffect(() => {
const controller = new AbortController();
axios
.get(url2 + `${pokemon.name}`, { signal: controller.signal })
.then((res) => setHabitat(res.data.habitat.name))
.then((res) =>
setDescriptions(
res.data.flavor_text_entries.map((ob) => ob.flavor_text)
)
)
.catch((err) => {
if (axios.isCancel(err)) {
} else {
console.log("warning your useEffect is behaving");
}
});
return () => {
// cancel the request before component unmounts
controller.abort();
};
}, [pokemon]);
I tried console logging descriptions or descriptions[0] but that doesn't work.
Since you only setting up the state from those data and it doesn't looks like the second result need to wait the result from the first to perform you can do both on the same response/promise :
useEffect(() => {
const controller = new AbortController();
axios
.get(url2 + `${pokemon.name}`, { signal: controller.signal })
.then((res) => {
setHabitat(res.data.habitat.name))
const flavorTextEntrieList = res.data.flavor_text_entries;
setDescriptions(flavorTextEntrieList.map((ob) => ob.flavor_text))
})
.catch((err) => {
if (axios.isCancel(err)) {
} else {
console.log("warning your useEffect is behaving");
}
});
return () => {
// cancel the request before component unmounts
controller.abort();
};
}, [pokemon]);
Each then need to return something to be handled in next chainable then. Replace .then((res) => setHabitat(res.data.habitat.name)) with .then((res) => { setHabitat(res.data.habitat.name); return res; })

Review and rating implementation of the doctor

Issue: State variable inside a api call is not getting updated
Description: Hi I am trying to implement the doctor review part of the application. where anyone can write the review of the doctor. when i am writing the review on submission of review am calling review creation api using axios.post inside then i am trying to update the review count state variable but it is not getting updated it is showing old value not the latest one
initial review count =1
submit a new review
review count should be 2
but when logging it is showing 1
how can i call a single api call to update review count inside the submit review. currently i am calling put api 2 times to update review count and recommendation review count
also i want to update doctor object with reviewcount and reco review count and pass this other page
doctor={...doctor,review_count:reviewcount,recommendation_count:recoreviewcount}
navigation.navigate("DoctorDetail",doctor);
as state variable is not getting updated it is holding the old value
review_count:reviewcount,recommendation_count:recoreviewcount
Example code....
----------
const ShareYourStory = ({ navigation, route }) => {
const doctor = route.params;
const [recovalue, setRecoValue] = useState();
const [waittimevalue, setWaitTimeValue] = useState(0);
const [isfriendliness, setFriendliness] = useState(false);
const [isexplainhealthissue, setExpIssue] = useState(false);
const [issatisfaction, setSatisfaction] = useState(false);
const [issue, setIssue] = useState("");
const [reviewcomment, setReviewComment] = useState("");
const [reviewcount, setReviewCount] = useState(0);
const [recoreviewcount, setRecoReviewCount] = useState(0);
//comment
const submitFeedback = () => {
console.log("submit patient feedback");
console.log('reviewcountstatus',reviewcountstatus,'recoreviewcountstatus',recoreviewcountstatus);
var most_happy_about = "";
if (isfriendliness)
most_happy_about = most_happy_about + "Doctor Friendliness,";
if (isexplainhealthissue)
most_happy_about = most_happy_about + "Explaination of Health Issue,";
if (issatisfaction)
most_happy_about = most_happy_about + "Treatment satisfaction";
var reviewer_name = "Vikram";
let reviewinfo = {
doctor: `${doctor._id}`,
reviewer_name: `${reviewer_name}`,
is_doctor_recommended: `${recovalue}`,
review: `${reviewcomment}`,
issue_for_visit: `${issue}`,
most_happy_about: `${most_happy_about}`,
};
//console.log(reviewinfo);
axios
.post(`${host}/api/v1/reviews`, reviewinfo)
.then((res) => {
if (res.status == 201) {
// fetch no of review count from review table based on doctor id
var reviewurl = `${host}/api/v1/reviews?doctor=${doctor._id}`;
console.log("reviewurl", reviewurl);
axios
.get(reviewurl)
.then((res) => {
console.log("reviewcount", res.data.count);
if (res.data.count > 0) {
setReviewCount(res.data.count);
const updatereviewcount = {
review_count: `${reviewcount}`
};
axios
.put(`${host}/api/v1/doctors/${doctor._id}`, updatereviewcount)
.then((res) => {
if(res.status == 200 || res.status == 201) {
console.log('status',res.data);
}
})
.catch((error) => {
console.log('error',error);
})
}
})
.catch((err) => {
console.log(err);
});
// calculate doctor_recommended_percent value
var recoreviewurl = `${host}/api/v1/reviews?doctor=${doctor._id}&is_doctor_recommended=Yes`;
console.log("recoreviewurl", recoreviewurl);
axios
.get(recoreviewurl)
.then((res) => {
console.log("recoreviewcount", res.data.count);
if (res.data.count > 0) {
setRecoReviewCount(res.data.count);
const updaterecoreviewcount = {
doctor_recommended_percent: `${recoreviewcount}`
};
axios
.put(`${host}/api/v1/doctors/${doctor._id}`, updaterecoreviewcount)
.then((res) => {
if(res.status == 200 || res.status == 201) {
console.log('status',res.data);
}
})
.catch((error) => {
console.log('error',error);
})
}
})
.catch((err) => {
console.log(err);
});
console.log(recoreviewcountstatus,reviewcountstatus);
}
})
.catch((err) => {
console.log("feedback review creation failed", err);
});
};
}

How do I update an array which is within an async function in a useEffect hook?

I'm working on a React app, which connects to a firebase database. Part of the app reads in a list of Items belonging to the current User, and then reads a config object in for each one, and finally updates the items in state. I'm having trouble getting it to work - I seem to keep getting
My component is:
const Dashboard = () => {
const authUser = useContext(AuthUserContext);
const firebase = useContext(FirebaseContext);
const [error, setError] = useState<string|null>(null);
const [firebaseToken, setFirebaseToken] = useState<string|null>(null);
const [items, setItems] = useState<Array<ItemModel>>([]);
// On first render, get all Items
useEffect(() => {
if(!authUser) return;
if(!firebase) return;
let userId = authUser.id;
const getItems = () => {
firebase.doGetIdToken()
.then((token) => {
// Save token so it can be passed down
setFirebaseToken(token);
url = "items/" + userId;
Client.getData(url, token)
.then((itemResults:Array<ItemModel>) => {
// Get config for each Item
// Set up an empty array to hold the new data
const itemResultsWithConfigs:Array<ItemModel> = []
// Now get the config for each item
itemResults.forEach((item:ItemModel) => {
// Get config for this Item
url = "/items/config/" + item.id;
Client.getData(url, token)
.then((newConfig:ConfigModel) => {
let newItem:ItemModel = {
...item,
config: newConfig
}
// Add full item to list & update list
itemResultsWithConfigs.push(newItem);
})
})
setItems(itemResultsWithConfigs);
})
});
})
})
.catch(() => setError("Unable to connect to database"))
}
getItems();
}, [authUser, firebase])
return (
<>
<ul>
{
items.map((item:ItemModel) => {
return <li key={item.id}>{item.name}</li>
})
}
</ul>
</>
);
}
export default Dashboard;
Client.getData is:
async function getData(path:string, token:string) {
const object:AxiosRequestConfig = {
...obj,
method: 'GET',
headers: {
...obj.headers,
'Authorization': `Bearer ${token}`,
},
};
try {
const response:AxiosResponse = await axios.get(`${baseUrl}${path}`, object);
checkStatus(response);
const parsedResult = parseJSON(response);
return parsedResult;
} catch (error) {
throw error;
}
}
The problem is that the async function (getData) is returning at different times, therefore the array of items is being overwritten some of the time. Currently this is only rendering one or two of the items instead of the 3 that I know should be there.
How do I approach this?
Since itemResultsWithConfig is derived asynchronously, a good idea is to map and wait for all the promises to resolve using Promise.all
const getItems = () => {
firebase.doGetIdToken()
.then((token) => {
// Save token so it can be passed down
setFirebaseToken(token);
url = "items/" + userId;
Client.getData(url, token)
.then((itemResults:Array<ItemModel>) => {
// Get config for each Item
// Set up an empty array to hold the new data
// Now get the config for each item
let promises = itemResults.map((item:ItemModel) => {
// Get config for this Item
url = "/items/config/" + item.id;
return Client.getData(url, token)
.then((newConfig:ConfigModel) => {
let newItem:ItemModel = {
...item,
config: newConfig
}
return newItem;
})
})
Promise.all(promises).then((itemResultsWithConfigs:Array<ItemModel>) => setItems(itemResultsWithConfigs))
})
});
})

Multiple nested axios calls don't resolve as expected

As described in comments between my code snippet, the asynchronicity is not working as expected. For each id, an object/item should return but it only returns one item since my async await isn't implemented properly. What could be a possible workaround?
Thanks in advance
useEffect(() => {
axios.get('url-here').then((res) => {
res.data.favProperties?.map((el) => {
console.log(el) // this returns multitple id's of saved/liked items
axios.get('url-here').then(async (r) => {
if (r.data) {
console.log(r.data) // Problem starts here
// This returns the full object of the liked items
// But only one object is returned, not every object for which an id was stored
await storageRef
.child(r.data.firebaseRef + '/' + r.data.images[0])
.getDownloadURL()
.then((url) => {
// Here i need to fetch the image for each object
console.log(url)
})
.catch((err) => console.log(err))
}
})
})
})
}, [])
I think breaking down your operations into functions will prevent this Promise Hell. I would recommend using async await for these kinda operations. Also I was confused about the last part of console logging the download URL, by my guess you're trying to save all the download URLs for these liked items in an array.
useEffect(() => {
firstFunction();
}, []);
const firstFunction = async () => {
const { data } = await axios.get("url-here");
const favProperties = data.favProperties;
const fetchedUrls = await Promise.all(
favProperties?.map(async (el) => (
await secondFunction(el.id) /** use el to pass some ID */
))
);
};
const secondFunction = async (someId) => {
/** your second URL must point to some ID (or some parameters) specific API otherwise
running same op in a loop without variations doesn't make any sense */
const { data } = await axios.get(`some-other-url/${someId}`);
if (data) {
console.log(data);
const fetchedUrl = await storageThing(data);
return fetchedUrl;
}
};
const storageThing = async ({ firebaseRef, images }) => {
try {
const downloadURL = await storageRef
.child(firebaseRef + "/" + images[0])
.getDownloadURL();
console.log(downloadURL);
return downloadURL;
} catch (error) {
console.log(error);
return '';
}
};

Expo React Native watchPositionAsync fires multiple times

I am using watchPositionAsync to get the location of the user and update the state, this works fine but when the app is in the background and re-opened, it seems to fire multiple times causing my screen to refresh, it also ignored the 10 second interval.
Is there any way to fix this? It seems to be a problem on Android and iOS
async componentDidMount() {
this._getlocation();
}
_getlocation = async () => {
const { status } = await Permissions.askAsync(Permissions.LOCATION)
this.watchLocation = await Location.watchPositionAsync(
{
distanceInterval: 15,
accuracy:6,
timeInterval: 10000
},
location => {
let coords = location.coords;
this.setState({userLocation: location})
this.fetchItems();
},
error => console.log(error)
);
}
Try to add "isRunning: false" to the component state and try to use the following edition:
async componentDidMount() {
if(this.state.isRunning === false) { // <----- ADDED ------------------------
this._getlocation();
}
}
_getlocation = async () => {
const { status } = await Permissions.askAsync(Permissions.LOCATION);
this.setState({ isRunning: true }); // <----- ADDED ------------------------
this.watchLocation = await Location.watchPositionAsync(
{
distanceInterval: 15,
accuracy:6,
timeInterval: 10000
},
location => {
let coords = location.coords;
this.setState({userLocation: location})
this.fetchItems();
},
error => console.log(error)
);
}

Resources