Can't figure out why array length is undefined - reactjs

I keep receiving the following message in my console, "Uncaught (in promise) TypeError: Cannot read property 'length' of undefined"
quotesData.quotes should be the key for an array, however, so I'm unsure as to why its length property is undefined.
quotesData should be a JSON object that looks like: { "quotes": [Object1, Object2, ...etc.]}
Is there something wrong with how I'm using axios? I'm still very new to programming in general and quite new to react.js
getQuote() {
let _this = this;
_this.serverRequest =
axios
.get("https://raw.githubusercontent.com/dwgillette/quotes/master/library")
.then(function(quotesData) {
console.log(quotesData);
let newQuote = quotesData.quotes[Math.floor(Math.random() * quotesData.quotes.length)];
_this.setState({
quote: newQuote.quote,
author: newQuote.author
});
})
}

So the data you want is actually going to be on a .data attribute of the response. So if you fix your code up like this you will be good to go :)
getQuote() {
let _this = this;
_this.serverRequest =
axios
.get("https://raw.githubusercontent.com/dwgillette/quotes/master/library")
.then(function(q) {
quotesData = q.data;
console.log(quotesData);
let newQuote = quotesData.quotes[Math.floor(Math.random() * quotesData.quotes.length)];
_this.setState({
quote: newQuote.quote,
author: newQuote.author
});
})
}

Because the promise resolves to response object. Try doing:
getQuote() {
let _this = this;
_this.serverRequest =
axios
.get("https://raw.githubusercontent.com/dwgillette/quotes/master/library")
.then(function(response) {
let newQuote = response.data.quotes[Math.floor(Math.random() * response.data.quotes.length)];
_this.setState({
quote: newQuote.quote,
author: newQuote.author
});
})
}

Here is a screenshot of the response object you are getting back.
I refactored your code to work. you need to use res.data.
getQuote = () => {axios.get("https://raw.githubusercontent.com/dwgillette/quotes/master/library")
.then(res => {
let newQuote =
res.data.quotes[Math.floor(Math.random() * res.data.quotes.length)];
this.setState({
quote: newQuote.quote,
author: newQuote.author
});
});
};

Related

Array returning undefined in Vue from indexedDB

The console.log in my indexedDB works and returns the result that I want; an array of objects that is currently in the store. So my code there is correct. I'm going to use this information to build a table. However, in Vue it returns undefined. I'm trying to set the leagues array in Vue to equal the result array that indexedDB gives, but it returns undefined.
This is the code in Vue:
<script>
import * as db from "../db/db.js";
export default {
name: "leaguesTable",
data: function() {
return {
leagues: []
};
},
created: function() {
this.leagues = db.getAllInStore("meta", "leagues");
console.log(this.leagues);
}
};
</script>
This is my indexedDB code:
function getAllInStore(dbName, storeName) {
let db;
var request = indexedDB.open(dbName, 1);
request.onerror = function(event) {
alert("Database error" + event.target.errorCode);
};
request.onsuccess = function(event) {
db = event.target.result;
let tx = db.transaction(storeName, "readonly");
tx.onerror = function(event) {
alert("Transaction error" + event.target.errorCode);
};
let store = tx.objectStore(storeName);
let result = store.getAll();
tx.oncomplete = function() {
alert("This should work");
console.log(result.result);
return result.result;
};
};
}
In your created hook you need to make sure to return a value from db.getAllInStore so that this.leagues assumes that value.
Next, In the getAllInStore function result.result gets returned from the transaction but not within onComplete or the enclosing getAllInStore function.
Since the db uses event hooks like onError and onComplete, Returning the request won't give you the result of the call to the db. In order to return the value of an async operation in javascript, typically callbacks or promises are used. The example below makes use of promises to solve the issue.
Vue JS:
<script>
import * as db from "../db/db.js";
export default {
name: "leaguesTable",
data: function() {
return {
leagues: []
};
},
// async is necessary to use await
created: async function() {
// await is es2016 syntactic sugar for retrieving the value of a promise
this.leagues = await db.getAllInStore("meta", "leagues");
console.log(this.leagues);
}
};
</script>
IndexDB:
function getAllInStore(dbName, storeName) {
// resolve param is a function that signifies a successful operation
// reject param is a function that should be called whenever a check or error occurs
return new Promise((resolve, reject) => {
let db;
let request = indexedDB.open(dbName, 1);
request.onerror = (event) => reject(event);
request.onsuccess = (event) => {
db = event.target.result;
let tx = db.transaction(storeName, "readonly");
request.onerror = (event) => reject(event);
let store = tx.objectStore(storeName);
let result = store.getAll();
tx.oncomplete = (result) => resolve(result.result);
};
});
}
Further Reading:
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises

Angular: What's the correct way to return Observable?

I have the following method which isn't working correct:
getProducts(): Observable<Product[]> {
let PRODUCTS: Product[];
this.http.get(this.base_url + "api/products")
.subscribe(
(data) => {
for(var i in data) {
PRODUCTS.push(new Product(data[i].id, data[i].name, data[i].category, data[i].description, data[i].price, data[i].amount));
}
},
(error) => {
console.log(error);
});
return of(PRODUCTS);
}
The error I'm getting is this:
TypeError: Cannot read property 'push' of undefined
Now, I know that the PRODUCT array is not accessable from within the subscribe function, but I cannot get the correct solution for it.
Can anyone help me with that. I want to return an Observable<Product[]>.
Thank you in advance!
Edit: Updated to account for the fact that the API seems to return an array-like object rather than a true array.
You want to use map:
getProducts(): Observable<Product[]> {
return this.http.get(this.base_url + "api/products")
.map(data => {
let products = [];
for (let i in data) {
products.push(new Product(data[i].id, data[i].name, data[i].category, data[i].description, data[i].price, data[i].amount));
}
return products;
})
.do(null, console.log);
}
Since #pixelbit's comment keeps getting upvotes despite being wrong, here's an example showing why it is wrong:
// Fakes a HTTP call which takes half a second to return
const api$ = Rx.Observable.of([1, 2, 3]).delay(500);
function getProducts() {
let products = [];
api$.subscribe(data => {
for (let i in data) {
products.push(data[i]);
}
});
return Rx.Observable.of(products);
}
// Logs '[]' instead of '[1, 2, 3]'
getProducts().subscribe(console.log);

Why this setState is not a function in ComponenDidMount?

I am trying to fetch ordered data from Firebase and set it to state highscoreArray but it gives error "undefined is not a function (evaluating 'this.setState({ highscoreArray:sortedHighscores })')
componentDidMount() {
const reference = database.ref("highscores");
// Pushing sorted data to highscoreArray.
reference.orderByChild("highscore").limitToLast(3).on("value", function (snapshot) {
sortedHighscores = [];
snapshot.forEach(function (child) {
sortedHighscores.push({
"username": child.val().username,
"score": child.val().highscore
});
});
sortedHighscores = sortedHighscores.reverse();
console.log("sortedh", sortedHighscores); // fetch success
this.setState({highscoreArray: sortedHighscores}); // gives error
});
}
One of the major advantages of arrow functions is that it does not have it's own this value. It's this is lexically bound to the enclosing scope.
class Logger {
dumpData(data) {
var _this = this;
// this dumps data to a file and get the name of the file via a callback
dump(data, function (outputFile) {
_this.latestLog = outputFile;
});
}
}
// using arrow functions
class Logger {
dumpData(data) {
dump(data, outputFile => this.latestLog = outputFile);
}
}
1.this not accessible within loop so use variable let that = this the use that wherever you need this in this function.
componentDidMount() {
const reference = database.ref("highscores");
let that = this // here your variable declaration
// Pushing sorted data to highscoreArray.
reference.orderByChild("highscore").limitToLast(3).on("value", function (snapshot) {
sortedHighscores = [];
snapshot.forEach(function (child) {
sortedHighscores.push({
"username": child.val().username,
"score": child.val().highscore
});
});
sortedHighscores = sortedHighscores.reverse();
console.log("sortedh", sortedHighscores); // fetch success
that.setState({highscoreArray: sortedHighscores}); // gives error
});
}
Hope this will help you :) happy coding!
Inside the function callback the this has a different context. Either use an arrow function, or store a reference outside:
Arrow:
reference.orderByChild("highscore").limitToLast(3).on("value", (snapshot) => { ... });

How to assign snap.val() to the global variable?

I want to assign snap.val() to this.Productslike this.Products= snap.val(); but this.Products is undefined in that scope.
Products: FirebaseListObservable<any>;
constructor(){
}
ionViewDidLoad(){
this.angularFire.database.list('/Products').$ref.orderByChild('uid')
.equalTo('NW1Kq4WB7ReUz2BNknYWML9nF133').on('child_added', function(snap){
console.log(snap.val().name);
//this.Products= snap.val();
});
}
I tried the following code when snap is returned ,but I receive this message -- No index defined for uid:
snap.forEach(SnapShot=>{
console.log(SnapShot.val().name)
My Firebase database:
"Products" : {
"-Kbx0i-TFeTyRbNZAZ_8" : {
"category" : "1",
"detail" : "xxxxx details",
"name" : "xxxxx",
"uid" : "NW1Kq4WB7ReUz2BNknYWML9nF133"
}
Please help. Thanks.
The directly answer the question you asked, you can use an ES6 arrow function:
let query = this.angularFire.database.list('/Products').$ref.orderByChild('uid')
.equalTo('NW1Kq4WB7ReUz2BNknYWML9nF133');
query.on('child_added', (snap) => this.Products= snap.val());
Or for ES5 compatibility, declare this as a variable:
let self = this;
let query = this.angularFire.database.list('/Products').$ref.orderByChild('uid')
.equalTo('NW1Kq4WB7ReUz2BNknYWML9nF133');
query.on('child_added', function(snap) {
self.Products= snap.val();
});
But in reality, this is an XY problem and you don't want what you think you want here.
What you've done is reimplement the list yourself, and defeat the entire purpose of AngularFire2, which handles all this synchronization on your behalf.
Additionally, you've mis-used child_added by assigning each record you get back (you get an array of results, not exactly one) to this.products, when you probably wanted to set this.products = [] and then use this.products.push(snap.val()) for each child_added invocation.
So what you really want here, is to use AngularFire's built-in queries and avoid this entire mess :)
this.products = af.database.list('/Products', {
query: {
orderByChild: 'uid',
equalTo: 'NW1Kq4WB7ReUz2BNknYWML9nF133'
}
});
I did it in this way:
import firebase from "firebase";
const firebaseConfig = {
your firebaseConfig...
};
let app = firebase.initializeApp(firebaseConfig);
let database = firebase.database();
export async function readFromFirebase(userId, key) {
const ref = database.ref("users/" + userId + "/" + key);
const snapshot = await ref.once("value");
return snapshot.val();
}
async function main() {
console.log(await readFromFirebase(109512127, "userName"));
}
main();

update in firebase is not working(with React) [duplicate]

I am quite new to firebase and i am trying to retrieve particular data. and getting this error,
Please help me with this.
My code looks like this
var t1= this.password.value;
var user_id = this.state.comments[2];
firebase.database().ref(user_id).orderByChild("Password").equalTo(t1).on("child_added", (snapshot) => {
var datas1 = []
snapshot.forEach((data1) =>
{
datas1.push( {
username: data1.val().Username,
password: data1.val().Password,
});
});
this.setState ({
datas1: datas1,
});
});
console.log(this.state.datas1);
even the array datas1 is coming empty.

Resources