React Firebase read element - javascript

I'm trying to get the value stored in 'Nombre', but it doesn't work at all, am I doing something wrong?
class Appp extends React.Component {
constructor() {
super()
this.state = { name: 'hey' }
}
componentDidMount() {
const nameRef = firebase.database().ref().child('Anonimos').child('wx3czBh22dMQUTNDwD9l').child('Nombre')
nameRef.on('value', snapshot => {
this.setState({
name: snapshot.val()
})
})
}
render() {
return <h1>{this.state.name}</h1>
}
}

Your code uses the API for the Firebase Realtime Database. But the screenshot shows data in Cloud Firestore. While both databases are part of Firebase, they are completely separate and the API for one can't access the data in the other.
To access the data in Cloud Firestore, follow the documentation for that database: https://firebase.google.com/docs/firestore/quickstart
Something like:
componentDidMount() {
const nameRef = firebase.firestore().collection('Anonimos').doc('wx3czBh22dMQUTNDwD9l')
nameRef.onSnapshot(doc => {
this.setState({
name: doc.data().Nombre
})
})
}
Aside from the new syntax, the biggest change here is that this code reads the entire document, instead of just the Nombre field, and then sets just the name in the state. So while the end result is the same, this loads more data than your Realtime Database example would, since that API allow the loading of any node, while Cloud Firestore always loads complete documents.

Related

Multiple set state within multiple api call inside componentDidMount in ReactJS

I am new in React and trying to call multiple api call within componentDidMount function.
My code is
componentDidMount() {
Promise.all([
axios.get(<url1>),
axios.get(<url2>)
]).then(([res1, res2]) => {
// call setState here
const users = res1.data.users;
this.setState({ users: users});
const banks = res2.data.banks;
this.setState({ banks: banks});
console.log("Users")
console.log(users) // It works
console.log("Banks")
console.log(banks) // It works
})
}
render() {
console.log(this.state.users.length) // Gives length
console.log(this.state.banks.length) // Gives undefined
return (
<div className='popup'></div>
)
}
The problem is inside render function the second state banks length is undefined.
How can I do multiple setstate inside componentDidMount.
Any help is highly appreciated.
Update: Resolved
The mistake was
constructor(props) {
super(props);
this.state = {
users: [],
//MISSING BANKS array
}
}
You should set state in a single update, updating both values at the same time. Otherwise you are instructing React to initiate two renders, the first would contain users value and an undefined value for banks (depending on your initial state declaration). This render would be quickly followed by a second pass, in which both state values users and banks would be defined.
The below example should work as required, in a single render.
Promise.all([
axios.get(<url1>),
axios.get(<url2>)
]).then(([res1, res2]) => {
// call setState here
const users = res1.data.users;
const banks = res2.data.banks;
this.setState({ users, banks });
});
On the other hand, if for some strange requirement you actually want two sequential renders you can use setState's done callback; example below.
this.setState({ users }, () => {
this.setState({ banks });
});
This will ensure the first render is complete before requesting a new render via setState.

Having Trouble Loading Earlier Messages in React-native GiftedChat Chat App with Firebase

I have been working on a chat app using Gifted-Chat and a Firebase RealTime database (and running it with Expo). At this point, the basic messaging works, but I am trying to enable to app to load earlier messages when the user scrolls up and hits the button that appears (I am aware of the GiftedChat prop for this). Unfortunately, I have been having trouble doing this and am a bit stumped.
There are two separate problems I have been running up against that I am aware of.
Clicking the loadEarlier button gives me an undefined is not a function (near '...this.setState...' runtime error (clearly, something is wrong with the skeleton function I put there).
The bigger issues is that I am still not clear on how to download the n number of messages before the oldest messages currently loaded. I have looked at the GiftedChat example and this post for help, but must confess that I am still lost (the best I can figure is that I need to sort the messages, possibly by timestamp, somehow get the right range, then parse them and prepend them to the messages array in state, but I cannot figure out how to do this, especially the later parts).
The relevant parts of the code for my chat screen are below, as is a screenshot of the structure of my firebase database. I would appreciate any help regarding both of these issues.
// Your run of the mill React-Native imports.
import React, { Component } from 'react';
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import * as firebase from 'firebase';
// Our custom components.
import { Input } from '../components/Input';
import { Button } from '../components/Button';
import { BotButton } from '../components/BotButton';
// Array of potential bot responses. Might be a fancy schmancy Markov
// chain like thing in the future.
import {botResponses} from '../Constants.js';
// Gifted-chat import. The library takes care of fun stuff like
// rendering message bubbles and having a message composer.
import { GiftedChat } from 'react-native-gifted-chat';
// To keep keyboard from covering up text input.
import { KeyboardAvoidingView } from 'react-native';
// Because keyboard avoiding behavior is platform specific.
import {Platform} from 'react-native';
console.disableYellowBox = true;
class Chat extends Component {
state = {
messages: [],
isLoadingEarlier: false,
};
// Reference to where in Firebase DB messages will be stored.
get ref() {
return firebase.database().ref('messages');
}
onLoadEarlier() {
this.setState((previousState) => {
return {
isLoadingEarlier: true,
};
});
console.log(this.state.isLoadingEarlier)
this.setState((previousState) => {
return {
isLoadingEarlier: false,
};
});
}
// Get last 20 messages, any incoming messages, and send them to parse.
on = callback =>
this.ref
.limitToLast(20)
.on('child_added', snapshot => callback(this.parse(snapshot)));
parse = snapshot => {
// Return whatever is associated with snapshot.
const { timestamp: numberStamp, text, user } = snapshot.val();
const { key: _id } = snapshot;
// Convert timestamp to JS date object.
const timestamp = new Date(numberStamp);
// Create object for Gifted Chat. id is unique.
const message = {
_id,
timestamp,
text,
user,
};
return message;
};
// To unsubscribe from database
off() {
this.ref.off();
}
// Helper function to get user UID.
get uid() {
return (firebase.auth().currentUser || {}).uid;
}
// Get timestamp for saving messages.
get timestamp() {
return firebase.database.ServerValue.TIMESTAMP;
}
// Helper function that takes array of messages and prepares all of
// them to be sent.
send = messages => {
for (let i = 0; i < messages.length; i++) {
const { text, user } = messages[i];
const message = {
text,
user,
timestamp: this.timestamp,
};
this.append(message);
}
};
// Save message objects. Actually sends them to server.
append = message => this.ref.push(message);
// When we open the chat, start looking for messages.
componentDidMount() {
this.on(message =>
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, message),
}))
);
}
get user() {
// Return name and UID for GiftedChat to parse
return {
name: this.props.navigation.state.params.name,
_id: this.uid,
};
}
// Unsubscribe when we close the chat screen.
componentWillUnmount() {
this.off();
}
render() {
return (
<View>
<GiftedChat
loadEarlier={true}
onLoadEarlier={this.onLoadEarlier}
isLoadingEarlier={this.state.isLoadingEarlier}
messages={this.state.messages}
onSend={this.send}
user={this.user}
/>
</View>
);
}
}
export default Chat;
For your first issue, you should declare your onLoadEarlier with => function so as to get the current instance this i.e. your code should look like below:
onLoadEarlier = () => {
this.setState((previousState) => {
return {
isLoadingEarlier: true,
};
}, () => {
console.log(this.state.isLoadingEarlier)
this.setState((previousState) => {
return {
isLoadingEarlier: false,
};
});
});
}
Also, setState is asynchronous in nature, so you should rather depend on the second parameter of the setState i.e. the callback to ensure that the next lines of code execute synchronously.
Lastly, if you are using class syntax then you should declare the state in constructor like below:
class Chat extends Component {
constructor (props) {
super (props);
state = {
messages: [],
isLoadingEarlier: false,
};
}
......
onLoadEarlier = () => {
this.setState((previousState) => {
return {
isLoadingEarlier: true,
};
}, () => {
console.log(this.state.isLoadingEarlier)
this.setState((previousState) => {
return {
isLoadingEarlier: false,
};
});
});
}
...
}
For loading the last messages from firebase , I recommend using limitToLast function on your reference. You should afterwards order the results by date before calling append in gifted chat.
For the second question, it should be the same with this question How Firebase on and once differ?
You can using filter feature in Firebase for example using createdAt field to compare with last loaded message to load more.

Using Map in React with firebase

Someone please help. I have combed all of the doc's, examined all similar questions and really cannot see what I am missing.
I am new to react and trying to map over documents returned from an asynchronous call from a firebase db.
Here is the code:
class Posts extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
let posts = [];
db.collection("Posts").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
posts.push({
data: doc.data(),
id: doc.id
});
});
});
this.setState({
data: posts
});
}
renderPosts() {
console.log(this.state.data);
return this.state.data.map((post, index) => {
console.log(post);
console.log(index);
return (
<div>
{index}
</div>
)
})
}
render() {
return(
<div>
{this.renderPosts()}
</div>
);
}
}
I am sure it is something super simple but I am pulling my hair out. If I examine my state I can see the data. console.log(this.state.data); inside renderPosts even displays exactly what I need to map over, but everything inside the map callback doesn't return and I end up with a single empty Any help would be greatly appreciated.
As Li357 commented, the setState call needs to happen inside the get() callback:
db.collection("Posts").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
posts.push({
data: doc.data(),
id: doc.id
});
});
this.setState({
data: posts
});
});
The reason for this is that data is loaded from Firestore asynchronously, and by the time you were calling setState the posts variable was still empty.
The easiest way to understand this type of asynchronous loading is with a few log lines:
console.log("Before starting to get documents");
db.collection("Posts").get().then(function(querySnapshot) {
console.log("Got documents");
});
console.log("After starting to get documents");
When you run this, the output is:
Before starting to get documents
After starting to get documents
Got documents
That is probably not the order you expected the output in. But it is the expected behavior: instead of blocking the code while the data is loaded (which would freeze the browser), the code after the get().then() block immediately executes. And then when the data is loaded, the code in the then callback is executed. That's why all code that needs the documents from the database needs to be inside the then callback.

Can't delete from state synced to firebase - ReactJS

I am having a problem with deleting from the state that is synced to Firebase. I think the problem is coming from my delete function.
This is how I am syncing the state to the Firebase
componentDidMount() {
base.syncState("recipes", {
context: this,
state: "recipes"
});
this.loadSampleData();
}
My delete function looks like this
deleteRecipe(index) {
const recipes = { ...this.state.recipes };
delete recipes[index];
this.setState({ recipes: recipes });
}
It worked without firebase but when I synced my state to firebase it stopped to work.
How to make it work with Firebase?
I just changed my deleteRecipe function like that and it started to work:
deleteRecipe(index) {
const recipes = { ...this.state.recipes };
recipes[index] = null;
this.setState({ recipes: recipes });
}

Setting fetched data from API in state won't work

I'm trying to build a news/article website for education purposes using Django and ReactJS.
Currently, I've created an article model in Django and set up an API for ReactJS to talk to. Every article has a headline, image, content, featured and quickreads properties. featured and quickreads are boolean values. I've successfully set up my ReactJS component to fetch all the articles however, I'm having trouble filtering the articles that have article.featured as true and article.quickreads also as true. Currently my component has three states: articles, featured and quickreads. This is how it currently looks:
class Api extends React.Component{
constructor(){
super();
this.state = {
articles: null,
featured: null,
quickreads: null
}
}
componentDidMount(){
fetch("http://127.0.0.1:8000/articles/articlesapi/").then(request => request.json()).then(response => this.setState({articles: response}))
var featured = this.state.articles.filter(article => article.featured === true)
var quickreads = this.state.articles.filter(article => article.quickreads === true)
this.setState({featured: featured, quickreads: quickreads})
}
render(){
return (
<p>Hello World</p>
)
}
}
Although the component gets all the articles it fails to update featured and quickreads. I get the following error:
Uncaught TypeError: Cannot read property 'articles' of undefined at componentDidMount (eval at <anonymous>)...
Why is this happening?
fetch is asynchronous, and thus articles is not set (and is null) when you try to filter it to set state. Instead, wait until the data is fetched:
fetch("http://127.0.0.1:8000/articles/articlesapi/")
.then(request => request.json())
.then(response => {
this.setState({
articles: response
featured: response.filter(article => article.featured === true),
quickreads: response.filter(article => article.quickreads === true)
});
});
And filter and set state along with setting articles after the data is fetched. I would, though, only store articles in state, and filtering when you need to do you don't end up having to sync up all the arrays to make sure they have the same data.

Categories

Resources