Firebase On Value get state react hooks - reactjs

i'm using firebase and react hooks, my problem is that I need to check if the driverId that i have is still in the drivers list, but the problem is that inside the event, inputs.driverId is null, even if it already have a value, and if a "print" the inputs variable all the flelds are like when i declared the initial state.
const initialState = {
driverId:'',
drivers: []
};
const [inputs, setInputs] = useState(initialState);
const getDrivers = () => {
const self = this;
const nameRef = firebase.database().ref().child('drivers').orderByChild('status');
nameRef.on('value', snapshot => {
var drivers = snapshot.val();
setInputs(inputs => ({...inputs, ['drivers']: drivers}));
console.log('dtiverId', inputs.driverId) // console response: dtiverId
})
}
anyvody that can help me, i need when the event excutes check if the select driver (driverId) is still in the list, but when i check driverId i get null, only inside the event

firebase.database() is an asynchronous call. You need to add async and await like so
const getDrivers = aysnc () => {
const self = this;
const nameRef = await firebase.database().ref().child('drivers').orderByChild('status');
nameRef.on('value', snapshot => {
var drivers = snapshot.val();
setInputs(inputs => ({...inputs, ['drivers']: drivers}));
console.log('dtiverId', inputs.driverId) // console response: dtiverId
})
}
What is happening in your code is that you are trying to use driverId before it is returned from firebase (which is why it is null). Async will block until firebase returns then your code will resume executing.

The firebase realtime database works asynchronously.
You should make use of async await or promises to get the data.
The examples in the
firebase docs show how to get the data using promises.

Related

Const is not defined -> Yet standard solutions don't work

I want to display a mapped list where "UserName" is an entry value from a Firebase Realtime Database corresponding to the author of each entry.
The following code, inside the get(UsernameRef).then((snapshot) =>{}) scope, returns an undefined reference error as expected, 'UserName' is assigned a value but never used and 'UserName' is not defined
const [RecipeLibrary, setRecipeLibrary] = React.useState([]);
React.useEffect(() => {
const RecipeLibraryRef = ref(db, "Recipes/");
onValue(RecipeLibraryRef, (snapshot) => {
const RecipeLibrary = [];
snapshot.forEach((child) => {
const AuthorUserId = child.key;
child.forEach((grandChild) => {
const UserNameRef = ref(db, "Account/" + AuthorUserId + "/username");
get(UserNameRef).then((snapshot) => {
const UserName = snapshot.val();
});
RecipeLibrary.push({
name: grandChild.key,
author: UserName,
...grandChild.val(),
});
});
});
setRecipeLibrary(RecipeLibrary);
console.log({ RecipeLibrary });
});
}, []);
I've tried:
Using a React state to pass the variable -> Can't use inside React useEffect
Exporting and Importing a separate function that returns the desired UserName -> return can only be used in the inner scope
Moving the list .push inside the Firebase get scope -> React.useState can no longer access the list
I'm hoping there is a simple solution here, as I am new.
Your time and suggestions would mean a lot, thank you!
Update:
I got the RecipeLibrary array to contain the desired "UserName" entry, named author by moving the array .push inside the .then scope. Here is a log of that array at set (line 59) and at re-render (line 104).
child.forEach((grandChild) => {
const UserNameRef = ref(db, "Account/" + AuthorUserId + "/username");
get(UserNameRef).then((snapshot) => {
const UserName = snapshot.val();
RecipeLibrary.push({
name: grandChild.key,
author: UserName,
authorId: AuthorUserId,
...grandChild.val(),
});
});
});
});
setRecipeLibrary(RecipeLibrary);
console.log(RecipeLibrary);
However, now the mapped list is not rendering at all on screen.
Just some added context with minimal changes to original code, been stuck on this so long that I'm considering a full re-write at this point to jog my memory. Oh and here is the bit that renders the mapped list in case:
<Box width="75%" maxHeight="82vh" overflow="auto">
{RecipeLibrary.map((RecipeLibrary) => (
<Paper
key={RecipeLibrary.name}
elevation={3}
sx={{
etc...
This is a tricky one - the plainest option might be to move push() and setRecipeLibrary() inside the then() callback so they're all within the same scope, but that would have some terrible side effects (for example, triggering a re-render for every recipe retrieved).
The goal (which you've done your best to achieve) should be to wait for all the recipes to be loaded first, and then use setRecipeLibrary() to set the full list to the state. Assuming that get() returns a Promise, one way to do this is with await in an async function:
const [RecipeLibrary, setRecipeLibrary] = React.useState([]);
React.useEffect(() => {
const RecipeLibraryRef = ref(db, "Recipes/");
onValue(RecipeLibraryRef, (snapshot) => {
// An async function can't directly be passed to useEffect(), and
// probably can't be accepted by onValue() without modification,
// so we have to define/call it internally.
const loadRecipes = async () => {
const RecipeLibrary = [];
// We can't use an async function directly in forEach, so
// we instead map() the results into a series of Promises
// and await them all.
await Promise.all(snapshot.docs.map(async (child) => {
const AuthorUserId = child.key;
// Moved out of the grandChild loop, because it never changes for a child
const UserNameRef = ref(db, "Account/" + AuthorUserId + "/username");
// Here's another key part, we await the Promise instead of using .then()
const userNameSnapshot = await get(UserNameRef);
const UserName = userNameSnapshot.val();
child.forEach((grandChild) => {
RecipeLibrary.push({
name: grandChild.key,
author: UserName,
...grandChild.val(),
});
});
}));
setRecipeLibrary(RecipeLibrary);
console.log({ RecipeLibrary });
};
loadRecipes();
});
}, []);
Keep in mind that Promise.all() isn't strictly necessary here. If its usage makes this less readable to you, you could instead execute the grandChild processing in a plain for loop (not a forEach), allowing you to use await without mapping the results since it wouldn't be in a callback function.
If snapshot.docs isn't available but you can still use snapshot.forEach(), then you can convert the Firebase object to an Array similar to Convert A Firebase Database Snapshot/Collection To An Array In Javascript:
// [...]
// Change this line to convert snapshot
// await Promise.all(snapshot.docs.map(async (child) => {
await Promise.all(snapshotToSnapshotArray(snapshot).map(async (child) => {
// [...]
// Define this somewhere visible
function snapshotToSnapshotArray(snapshot) {
var returnArr = [];
snapshot.forEach(function(childSnapshot) {
returnArr.push(childSnapshot);
});
return returnArr;
}
Note that if get() somehow doesn't return a Promise...I fear the solution will be something less straightforward.

Return data from Async function React Native Redux

I am having trouble with accessing the data after fetching it with SecureStore in Expo for react-native.
Here is the simple code:
const infoofuser = SecureStore.getItemAsync('userInfo').then(value =>
console.log(`this is the vlaue from infouser: ${value}`),
);
console.log(`infoouser: ${JSON.stringify(infoofuser)}`);
the first infoofuser constant definition returns the object of the intended data.
console.log(`infoouser: ${JSON.stringify(infoofuser)}`);
however returns {"_U":0,"_V":0,"_W":null,"_X":null} which U understand is a promise. I would like to simply get the data that comes from the SecureStore call and use it to set my initialState in redux.
const infoofuser = SecureStore.getItemAsync('userInfo').then(value =>
value
);
this does not work either to access the data
You can use async method using async/await. Try this:
const userInfo = useSelector(state => state.userInfo);
const getData = async () => {
try {
const infoofuser = await SecureStore.getItemAsync('userInfo');
console.log('infoofuser:', infoofuser)
/// strore on redux
} catch (err) {
// handle error
}
}
useEffect(() => {
getData()
}, [])
if (!userInfo) return null
//render something else
You can check the Expo Secure Store docs for reference.

How to fetch API and store some value and then fetch again with that stored value in React

Currently I am fetching some data and I save the response via useState. The response is saved but the problem is the second fetch takes place before the state was saved making that value undefined.
I tried to do like this:
useEffect(() => {
fetchTrackingData().finally(() => {
console.log("Destination", destination.coordinates);
fetchDriverLocation().finally(() => {
setLoading(false);
});
});
}, []);
Basically fetchTrackingData function has destination coordinates which I save with:
const [destination, setDestination] = useState('');
In the fetchTrackingData function I am setting the state after receiving the response. After fetchTrackingData is done fetching and it has saved state of destination I then use finally() method and then call the second fetch which is fetchDriverLocation but it has destination undefined and even I logged to see if the value gets saved after finally() but it is undefined in log as well.
How can I store the value from one API in that API call and then use that value in another API call?
As another answer has stated, you are experiencing a race condition because setState in react is often asynchronous.
My suggestion would be to use two useEffects to accomplish this:
useEffect(() => {
fetchTrackingData();
}, []);
useEffect(() => {
console.log("Destination", destination.coordinates);
fetchDriverLocation().finally(() => {
setLoading(false);
});
}, [destination]);
The above will wait until destination changes before moving on with fetching the driver location
What you're experiencing is a race condition.
When you call fetchTrackingData, it makes the HTTP request to your API, gets a response and then the last thing it does is saves destination to the state. But when you call setDestination, this is another asynchronous task that is kicked off, then you move directly to trying to access it from the state (which it may or may not be there, depending on how long it takes to set the state).
My recommendation would be to either not rely on state for this process (perhaps a variable will do? or even just passing along the result of your http request) or to lean more heavily on chaining these Promises and have the setting of the state be included in the Promise chain. However, I would favour the former suggestion
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
and: Chain React setState callbacks
You can try with the async await methods and call the other APIs after the first one is done.
Example :
function App() {
const [data, setData] = useState();
const [data2, setData2] = useState();
useEffect(() => {
(async () => {
const response = await fetch("someURL");
const json = await response.json();
setData(json);
const responseList = await fetch("someURL" + json.data);
const jsonData = await responseList.json();
setData2(jsonData)
})();
}, []);
return (
<div>
Hello
</div>
);
}
export default App;
by using useEffect fetching data from firstURI and sending to secondURI
const [firstDate, setFirstData] = useState();
const [secondDate, setSecondData] = useState();
useEffect(() => {
(async () => {
const firstResponse = await fetch("firstURI");
const firstResponseData = await firstResponse.json();
setFirstData(firstResponseData);
const secondResponse = await fetch("secondURI" + firstResponseData.data);
const secondResponseData = await secondResponse.json();
setSecondData(secondResponseData)
})();
}, []);

Return value from Google Places getDetails callback asynchronously

Is it possible to make a Google Places callback from getDetails function asynchronus or return it's value?
Essentially, in my react app, I am trying to do the following:
pass a placeId to google places api
wait for google to bring back some place information (address, photos, etc)
make a call to my local api to do something with this data from google
What happens though is
google goes to get the data
the call to my api is made but google has not brought back the data yet
I have tried using useState however, the state is not updated right away and thus I am not sure how to "wait" for google places to finish getting data before continuing my call
example of what I am trying to do (obviously wrong)
const [value, setValue] = useState('')
const foo = async (placeId) => {
var service = new window.google.maps.places.PlacesService(
predictionsRef.current,
)
await service.getDetails(
{
placeId,
fields: ['photos'],
},
async place =>
await setValue(
place.photos[0].getUrl({ maxWidth: 1280, maxHeight: 720 }),
),
)
// I want 'value' to have google place data before the axios call
await axios.post(...)
}
I have looked at the following links so far but can't piece together what I need:
google places library without map
https://gabrieleromanato.name/javascript-how-to-use-the-google-maps-api-with-promises-and-async-await
useState set method not reflecting change immediately
You could make it this way,
const [value, setValue] = useState('')
const [placeID, setPlaceID] = useState(null)
a function to return a promise from google placeDetails API,
const getPlaceDetails = (ref) => {
return new Promise(function (resolve, reject) {
let placesService = new window.google.maps.places.PlacesService(ref);
placesService.getDetails(
{
placeId,
fields: ['photos'],
},
(place) => {
resolve(place.something);
}
);
});
};
an effect that triggers upon placeID change
useEffect(() => {
async function doStuff() {
let placesResponse = await getPlaceDetails(ref);
let myResponse = await yourAPICall(placesResponse);
setValue(placesResponse);
}
if(placeID !==null){
doStuff();
}
}, [placeID]);
I haven't tested this code, but hope this approach helps.

In react, object is seems to be as an empty object for using fetch

If I use a variable instead of using react state object inside a function is behaving weirdly for fetch. If i remove the const response..... and const data.... line things works just fine but If i use fetch function and use normal variable without using state this doesn't work. Why is that?
Object is showing normal if i log it from inside the function
Object is also showing normal if i use const variable
Object is showing like an empty object if i log it from outside the function
But this code works just fine..
const [services, setServices] = useState([]);
useEffect(() => {
const API_URL = "http://localhost:8080/getServiceName/3";
const loadData = async () => {
const apiData = [];
const response = await fetch(API_URL);
const data = await response.json();
data.services.map(service => (
apiData.push(service.service_name)
));
setServices(apiData);
}
loadData();
}, [location])
And notice that i can add more value to a const variable(const dummyLocation{in image} and const apiData in code)!, How??
loadData(); is asynchronous, so if you do
loadData()
console.log(dummyLocation)
then the console.log will happen before loadData completed. You probably just want to do
loadData().then(() => console.log(dummyLocation))
As for the questions regarding why you can do:
const apiData = [];
// ...
apiData.push(service.service_name)
the reason is that const doesn't mean the variable can't change (per se), but instead that the variable will never be reassigned - you can't do:
const apiData = [];
apiData = [service.service_name];
because that is assigning a new array to it.

Resources