Getting a specific object/item by id in Firebase - javascript

I am currently building a small web app with Firebase and React, but I am having trouble fetching for specific items in the Firebase from the React client-side.
That being said, I'm used to javascript, where a simple fetch might look something like:
const url = 'www.example.com/api/'
const id = '123'
fetch(url + id) <---specific
.then(res => res.json())
.then(result => this.setState({results: result})
.catch(err => console.log(err))
However, I haven't been able to find any documentation on something that looks similar with firebase.
A more specific issue below:
class StoryItem extends Component {
constructor(props) {
super(props);
this.state = {
story: this.props.location.myCustomProps
};
}
componentDidMount() {
//this should do a fetch request based on the
//params id to get the specific item in the firebase
//right now it is being passed as prop which is unreliable because when page refresh state is reset
//user should be able to access content
//without having to go to previous page
console.log(this.state.story)
}
One way I've tried to get the specific object from the firebase is this:
componentDidMount(props) {
const ref = firebase.database().ref("items");
ref.on("value", snapshot => {
let storiesObj = snapshot.val();
storiesObj
.child(this.props.match.params.id)
.then(() => ref.once("value"))
.then(snapshot => snapshot.val())
.catch(error => ({
errorCode: error.code,
errorMessage: error.message
}));
});
}
In this case, the error is
Any help would be appreciated, also, if anyone knows of any good documentation on firebase, feel free to send me a link.
Thank you

The trick is, you don't have to get the value of all items first, as you do.
You should locate the items ref, then lookup a child that you want and get the value of that child with .on or .once.
Something like that, based on your example code:
componentDidMount() {
firebase.database().ref("items");
.child(this.props.match.params.id)
.once("value")
.then(snapshot => snapshot.val())
.catch(error => ({
errorCode: error.code,
errorMessage: error.message
}));
}
For better understanding, let's take a look at the original code and try to figure out why it errors out:
componentDidMount(props) {
// ⬇️ this ref points to ALL items
const ref = firebase.database().ref("items");
// ⬇️ here we're asking for the value stored under the above ref
ref.on("value", snapshot => {
let storiesObj = snapshot.val();
/* so firebase gives us what we ask for, storiesObj
* is probably a huge js object with all the items inside.
* And since it's just a regular js object,
* it does not have a `child` method on it, thus calling .child errors out.
*/
storiesObj
.child(this.props.match.params.id)
.then(() => ref.once("value"))
.then(snapshot => snapshot.val())
.catch(error => ({
errorCode: error.code,
errorMessage: error.message
}));
});
}

Related

Create an array whenever an image is uploaded using url and then store a object into that array in firestore

I am able to get url, caption and id of an image using an object and then store them into firestore field but the problem is, I need them to be stored into an array so I can retrieve and display the images onto my UI. Currently firestore field is returning an array
I tried to merge class object with an array, push the object into the array and using normal object instead of class object.
`
class App {
constructor() {
this.posts = [];
this.post = {
id: cuid(),
caption: "",
image: "",
};
getFileUrl(name) {
const imageRef = storage.ref(name);
imageRef
.getDownloadURL()
.then((url) => {
this.post.image = url;
console.log(this.post.image);
this.savePosts();
this.render();
})
.catch((error) => {
console.log(error, "Error Occured");
});
}
savePosts() {
db.collection("users")
.doc(this.userId)
.set({
post: this.posts,
})
.then(() => {
console.log("Document successfully written!");
})
.catch((error) => {
console.error("Error writing document: ", error);
});
}
render() {
this.savePosts();
}
`
firestore
If think you're looking for:
this.post.image.push(url);
If you tried that and ran intro problems, edit your question to show the exact code you tried and where you got stuck (including any error message or the erroneous result you got).

firebase onSnapshot not updating data in real time

I'm learning firebase and I'm baffled because I'm following the tutorial to the dot, literally copy and paste and though the following code:
// init firebase
initializeApp(firebaseConfig)
// init services
const db = getFirestore()
// collection ref
const colRef = collection(db, 'books')
// real time collection data
onSnapshot(colRef, (snapshot) => {
let books = []
snapshot.docs.forEach(doc => {
books.push({ ...doc.data(), id: doc.id })
})
console.log(books)
})
// adding docs
const addBookForm = document.querySelector('.add')
addBookForm.addEventListener('submit', (e) => {
e.preventDefault()
addDoc(colRef, {
title: addBookForm.title.value,
author: addBookForm.author.value,
})
.then(() => {
addBookForm.reset()
})
})
in the tutorial results in data being updated live (changes are reflected in the console, as users add/removes books), on my app changes are reflected only after refreshing the page.
I did some digging but only managed to find people with similar issue but no answer, i tried changing snapshot.docs.forEach(doc => ...) to snapshot.docChanges().forEach(doc => ...). same result.

Reading a value from Realtime Firebase

I have the following json structure:
Within "all" node I have an attribute "drinkId" and I'm trying to move it outside that child node bringing it one level up.
I'm trying to read the value without any luck
const cocktailRef= firebase
.database()
.ref("Ratings");
cocktailRef.once("value", (snapshot) => {
snapshot.forEach((child) => {
const drinkIdPass = child.ref.child("all").child("drinkId").value();
child.ref.update({ drinkId: drinkIdPass });
})
})
I've tried different variants of ".value()", same problem
There isn't any value() method on a DataSnapshot. It's val() Try refactoring your code like this:
const cocktailRef= firebase.database().ref("Ratings");
cocktailRef.once("value").then(async (snapshot) => {
const updates = { }
snapshot.forEach((child) => {
const drinkIdPass = child.val().all.drinkId
updates[`${child.key}/drinkId`] = drinkIdPass
})
await cocktailRef.update(updates)
console.log("Data updated")
})

ReactJS / NextJS state array not rendering after setState

I am having trouble rendering my objects using .map() within React / NextJS.
I have a function where I get images from Firebase Cloud Storage, code below:
getImages = () => {
let firebase = loadFirebase()
var storageRef = firebase.storage().ref('chest')
let { state } = this
storageRef.listAll().then((result) => {
let data = []
result.items.forEach((imageRef) => {
imageRef.getDownloadURL().then((url) => {
data.push(url)
}).catch((error) => {
// Handle any errors
})
})
state.images = data
this.setState({ images: data })
}).catch((error) => {
// Handle any errors
})
}
This part seems to work as I do get data back and the state is updated, results as in screenshot:
Results after setState
I then map through images with the code below:
{ this.state.images.map((image, index) => {
return (
<img
key={ index }
src={ image }
alt=""
/>
)
})}
On the same page as this, I have other places where I get data from Firebase, set my states accordingly and render the objects using .map(). In those cases it works perfectly fine. Difference is that in those cases I use getInitialProps() to get my data from Firebase, whereas with the data from Cloud Storage I have a function, the getImages() function above, that is called on componentDidMount()
But in both cases the state is set in componentDidMount() and the final result returned of this.state looks like the screenshot attached.
Any help and / or improvements on this will be much appreciated.
You should never set the state values manually. You should just remove the line that sets the images in the state before calling setState. That line prevents the rendering since after that react can not detect any changes when you set the state using setState:
getImages = () => {
let firebase = loadFirebase()
var storageRef = firebase.storage().ref('chest')
storageRef.listAll().then((result) => {
let data = []
result.items.forEach((imageRef) => {
imageRef.getDownloadURL().then((url) => {
data.push(url);
this.setState({ images: data });
}).catch((error) => {
// Handle any errors
})
});
}).catch((error) => {
// Handle any errors
})
}

(reactjs) how to set a promise or set the current state into local storage

I'm trying to build a very simple app for searching articles and using the localstorage to display more info about each article but when I set to save into the session storage for some reason is saving the initial or previous state, I assume because I need to set async for this but I just can't figure how
This is how I have it right now, findArticleQuery() is called on the handleSubmit
useEffect(
() =>{
findArticlesQuery();
} ,[]
)
const findArticlesQuery = (query) => { //search function
axios.get(`url`)
.then(res => {
[res.data].map((val) =>(
setState(val)
))
}).catch(error => {
console.log(error.response)
});
}
const handleSubmit = (e) => {
e.preventDefault();
findArticlesQuery(e.target.search.value)
sessionStorage.setItem('localData', JSON.stringify(state)) //<--here I get the value of the previous state
e.target.search.value = '';
}
I need to use the session storage because I will have a detailed article component page.
Thank you guys!
You can get search result from findArticlesQuery() like below.
...
const findArticlesQuery = (query) => { //search function
return axios.get(`url`)
.then(res => {
setState(res.data)
return res.data
})
}
const handleSubmit = (e) => {
e.preventDefault();
findArticlesQuery(e.target.search.value)
.then(val => sessionStorage.setItem('localData', JSON.stringify(val)))
e.target.search.value = '';
}
For saving state in the localStorage with React Hooks, something I've been using and that is extremely convenient is useLocalStorage.
Disclaimer : I am not the creator of this lib.

Categories

Resources