Firestore Async function - reactjs

So I'm trying to use React Query with Firestore and usually this works pretty fine but recently I have been unable to make a function that returns a value.
const fetchProduct = async () => {
const querySnapshot = await getDocs(collection(db, "notes"));
const arr = [];
querySnapshot.forEach((doc) => {
setNotes(doc.data())
}).catch((error) => {
console.log(error);
return null;
});
return notes
}
I'm later trying to use this function with React Query like this -
const { isLoading, isError, data, error } = useQuery(['todos'], fetchProduct)
However the value of the {data} always return to undefined but however once the fetchProduct() function is called manually it all works.
Is there anything I'm missing or doing wrong?

Setting the state is an asynchronous operation in React (see docs), so the value of notes isn't set yet by the time your return notes runs.
If you want to return the value synchronously:
return querySnapshot.docs.map((doc) => doc.data());
More typically though, you'll want to put the code that depends on that return value into its own useEffect hook that depends on the notes state.
Also see:
The useState set method is not reflecting a change immediately
Why does calling react setState method not mutate the state immediately?
Is useState synchronous?

It should be like this, you should not set inside the foreach function
const fetchProduct = async () => {
const querySnapshot = await getDocs(collection(db, "notes"));
const notes = [];
querySnapshot.forEach((note) => {
notes.push({ ...note.data(), id: note.id })
}).catch((error) => {
console.log(error);
return null;
});
return notes;
}
// in the place of the calling the function
const notes = await fetchProduct();
setNotes(notes);
Note: don't use variable name as doc in foreach, instead use some other variable name like since doc is built in function of firebase you might have imported it

Related

Why my useEffect that tries to get blockchain data is looping infinitely and my async func still returns Promise pending

I am trying to use async await inside a useEffect hook getting some data from a testnet blockchain but I am getting 2 problems:
The async function returns a Promise, why is that? Shouldn't async await automatically resolve the promise and give me the data? I tried to solve it with Promise.resolve but not working, it still tells me campaigns is a Promise in pending state.
It enters in an infinite loop and I still do not get why.
Here is the code:
useEffect(() => {
const getCampaigns = async() => {
const campaigns = await factory.methods.getDeployedCampaigns().call()
return campaigns
}
const campaigns = getCampaigns();
setCampaigns(Promise.resolve(campaigns));
console.log('campaigns: ', campaigns);
})
You have no dependencies array.
useEffect(() => {
const getCampaigns = async() => {
const campaigns = await factory.methods.getDeployedCampaigns().call()
return campaigns
}
const campaigns = getCampaigns();
setCampaigns(Promise.resolve(campaigns));
console.log('campaigns: ', campaigns);
}, [])
Try this
useEffect(() => {
const getCampaigns = async() => {
const campaigns = await factory.methods.getDeployedCampaigns().call()
setCampaigns(campaigns);
}
getCampaigns();
}, []);
The empty array in useEffect call makes it behave like component did mount and only executes once (assuming factory methods are initialized on mount) and since the getDeployedCompanigns Promise is already resolved I'm simply setting the state in the getCampaigns function.
Read this article for details: https://devtrium.com/posts/async-functions-useeffect

React-Query Prefetch query doesnt return value?

const getQueryService = () => {
return {
login: async (id): Promise<AuthLoginGoogleResponse> => {
try {
const result = await authApi.loginGooglePost({
idToken: {
id_token: id,
},
});
return result;
} catch (error) {
console.error("Google Login Fail", error);
}
},
};
};
// Mutation is only for updating and creating and deleting
const getMutationService = () => {
return {};
};
const useGoogleLogin = () => {
const queryClient = useQueryClient();
const queryService = getQueryService();
// const { data, isLoading } = useQuery('auth', queryService.login)
const mutationService = getMutationService();
const fetchLoginData = async (
tokenId
): Promise<AuthLoginGoogleResponse | void> => {
return await queryClient.prefetchQuery("auth", async() => {
return await queryService.login(tokenId);
});
};
return fetchLoginData;
};
I am sending token.Id to API using Post request and I am calling it from component however when I run debugger, preFetchquery is not returning the value retuned from result in getqueryservice function.
Is there a reason why preFetchQuery is not returning the return value from getQueryService.login?
because that's what prefetching does. According to the docs (emphasis mine):
prefetchQuery is an asynchronous method that can be used to prefetch a query before it is needed or rendered with useQuery and friends. The method works the same as fetchQuery except that it will not throw or return any data.
So prefetchQuery just puts data in the cache so that it can be picked up later by useQuery, hence the name: pre-fetch.
If you wan to get data returned, you can use queryClient.fetchQuery instead - but you'd also need to handle errors in case the fetch fails.
To be honest, I'm not sure why you are trying to achieve though. Judging from the code, it looks like you're trying to execute a query when the user wants to login. Please keep in mind that this is not what queries are for. Logging someone in is a prime example for a mutation.

Updating the state correctly after fetch data from firestore

I am trying to use Firestore Snapchats to get real time changes on a database. I am using react-native-cli: 2.0.1 and react-native: 0.64.1 .
export const WelcomeScreen = observer(function WelcomeScreen() {
const [listData, setListData] = useState([]);
const onResult = (querySnapshot) => {
const items = []
firestore()
.collection("Test")
.get()
.then((querySnapshot) => {
querySnapshot.forEach(function(doc) {
const tempData = {key: doc.id, data: doc.data}
items.push(tempData);
});
setListData(items)
});
}
const onError = (error) => {
console.error(error);
}
firestore().collection('Test').onSnapshot(onResult, onError);
}
Every thing is working perfectly, until I use setListData to update the data list. The App does not respond anymore and I get a warning message each time I try to add or delete data from the database
Please report: Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code
I am creating a deadlock by setting the state this way?
First, you don't want to set up a snapshot listener in the body of your component. This results in a growing number of listeners, because every time you render you add a new listener, but every listener results in rendering again, etc. So set up the listener just once in a useEffect:
const [listData, setListData] = useState([]);
useEffect(() => {
function onResult(querySnapshot) {
// ...
}
function onError(error) {
console.log(error);
}
const unsubscribe = firestore().collection('Test').onSnapshot(onResult, onError);
return unsubscribe;
}, []);
In addition, your onResult function is going to get called when you get the result, and yet you're having it turn around and immediately doing a get to re-request the data it already has. Instead, just use the snapshot you're given:
function onResult(querySnapshot) {
const items = []
querySnapshot.forEach(function(doc) {
const tempData = {key: doc.id, data: doc.data()}
items.push(tempData);
});
setListData(items);
}

State not updating until external event

I am using useEffect to run a function that grabs data from firebase.
The data is grabbed fine, however the setState function does not seem to take effect until after another state has changed.... I thought using useEffect would run before first render.
function App() {
const [expenses, setExpenses] = useState([]);
const roster = [];
React.useEffect(() => {
const getRoster = async () => {
var db = firebase.firestore();
db.collection("Roster")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
var data = doc.data();
data.ID = doc.id;
roster.push(data);
});
});
console.log("setting roster");
setExpenses(roster);
console.log("roster", roster);
console.log("expenses", expenses);
};
getRoster();
}, []);
the console returns the following
setting roster
roster [all data from firebase here]
expenses [blank], **expenses is the state variable**
The expenses state only updates after I change some other state in the application. I've tried to work around this by changing some other states in the use effect function, no dice. Also I've tried passing the state as a dependency to the use effect. Nothing...
I must doing something wrong but I'm not sure what that is.
My goal is to have the expenses state updated on first page load.
setExpenses(roster); should be called inside .then as it .get is an async call and it takes some time so within this time your setExpenses(roster); gets called and its has the initial value of roster. So you should use your setExpenses(roster); as bellow
React.useEffect(() => {
const getRoster = async () => {
var db = firebase.firestore();
db.collection("Roster")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
var data = doc.data();
data.ID = doc.id;
roster.push(data);
});
console.log("setting roster");
setExpenses(roster);
console.log("roster", roster);
console.log("expenses", expenses);
});
};
getRoster();
}, []);
The setExpenses call should be placed directly after the querySnapshot.forEach call, but still within the .then((querySnapshot) => { ... } handler. Because it's currently placed after that handler, it is executed immediately on first render, not when the Firebase data is obtained.

setState never gets set in useEffect after API call responds with data

I'm trying to update state immediately after data comes in from the API. The data is coming in, I can see it using the console.log right below my API request. All of the data is right but for some reason, setState never sets in my hook. It just returns and empty array even after the console displays data.
const [experienceData, setExperienceData] = useState([]);
const { match = {} } = props;
useEffect(() => {
async function fetchData() {
if (Object.keys(match.params).length > 0) {
const response = await ApiService.getExperiences(match.params.experieneId);
console.log(response)
setExperienceData(response)
}
}
fetchData();
}, []);
I must be doing something wrong but I can't figure out what that is. Hoping someone on here has run into the same issue.
UPDATE: I just changed everything over the a class and duplicated the exact code on another file and ran into the exact same issue. The console updates with the data, but the setState on the line immediately after the data does not setState.
async componentDidMount() {
if (Object.keys(this.props.match.params).length > 0) {
const response = await ApiService.getExperiences(this.props.match.params.experieneId);
console.log(response[0])
this.setState({ experienceData: response[0], occurrenceData: response[0].occurrences });
}
}
You have to useSetState in a proper way, the issue is in the setExperienceData
const [experienceData, setExperienceData] = useState({response:""});
const { match = {} } = props;
useEffect(() => {
async function fetchData() {
if (Object.keys(props.match.params).length > 0) {
const response = await ApiService.getExperiences(match.params.experieneId);
console.log(response)
setExperienceData(experienceData => ({ ...experienceData, response: response }));
}
}
fetchData();
}, []);
return(<div>check {experienceData.response}</div>)
I see you left the dependency array empty. This tells React to run this effect only once: when the component first renders. If you want your useEffect to respect your state hook, put setExperienceData inside the dependency array
const [experienceData, setExperienceData] = useState([]);
const { match = {} } = props;
useEffect(() => {
fetchData();
}, [props.match.params]);
const async fetchData = () => {
if (Object.keys(match.params).length > 0) {
const response = await ApiService.getExperiences(match.params.experieneId);
console.log(response)
setExperienceData([...response])
}
}
Could you please try passing [match.params] as the second argument to your useEffect.

Resources