I'm learning React and I'm trying to render 10 pieces of data from a specific API.
This is a function I wrote for iterating through the fetched data and taking the title and the image:
for (let i = 0; i < 10; i++) {
data.push({
title: someMethod(allData)[i].children[0].data,
image: someMethod(allData)[i].attribs.src,
});
}
I don't know why but one of the images gives me that error:
index.js:1 TypeError: Cannot read property 'attribs' of undefined
Which stops the whole rendering.
I wanted to try to put a temporary placeholder image so the rest of the images can be loaded without any errors.
I thought about adding conditions into the image line inside the loop but it didn't work for me. What's the right way to do that?
If I wasn't clear enough please comment what wasn't clear and I'll try to describe it better.
If someMethod is synchronous (based on your code sample it should be) then you can wrap your code in try...catch block like so:
for (let i = 0; i < 10; i++) {
try {
data.push({
title: someMethod(allData)[i].children[0].data,
image: someMethod(allData)[i].attribs.src,
});
} catch (err) {
data.push({
title: 'placeholder',
image: 'placeholder',
});
continue;
}
}
Here's more cleaner version of the above:
for (let i = 0; i < 10; i++) {
let title = '';
let image = '';
try {
title = someMethod(allData)[i].children[0].data;
image = someMethod(allData)[i].attribs.src;
} catch (err) {
image = 'failed to load or something';
}
data.push({title, image});
}
for (let i = 0; i < 10; i++) {
data.push({
title: someMethod(allData)[i].children[0].data,
image: someMethod(allData)[i].attribs?.src ?? defaultSrc,
});
}
docs: Optional_chaining and Nullish_coalescing_operator
You can solve the null pointer exceptions in 2 ways.
Manually check for truthy values using ? or &&
Create a simple utility function
Point 1 will solve your problem, but however if you start using it everywhere throughout your code it may not look tidy. I'd recommend to go by creating a utility function as below.
export const getSafe = (fn, defaultValue=null) => {
try {
return fn()
} catch (e) {
return defaultValue
}
}
You can then use it anywhere like..
var result = getSafe(() => object1.data.key)
You can use it for accessing any type of values to any depth. You can also pass a default value as 2nd argument if required. by default it is set as null
var resultArray = getSafe(() => array1.data.values, [])
Just remember, the 1st value should be passed as a function.
So in your case...
for (let i = 0; i < 10; i++) {
data.push({
title: getSafe(() => someMethod(allData)[i].children[0].data, "Default Title"),
image: getSafe(() => someMethod(allData)[i].attribs.src, "placehoder_img"),
});
}
Implement the map function.
The for loop will give an error at your component
In react if you need to render elements don't implement the for loop
datas.map(......)
Related
Inside the function in the screenshot, I am adding the data from the backend function to the array with axios.get. But when I go outside of axios, the values of the array I print are undefined.
I am getting string value from backend. I want to be able to use it in different methods by returning it. Please help me. I can't find the solution.
getReasonsForWaitingCustomer() {
this.adimKodlariLastString = "";
if (this.$route.params.status == "musteri-bekleniyor") {
axios.get(URL + "xxx/xxx?xxx=xxx)
.then(response => {
for (var i = 0; i < response.data.data.length; i++) {
if (this.stepCode.includes(response.data.data[i].adim_kodu) == false) {
this.stepCode.push(response.data.data[i].adim_kodu);
}
}
for (var j = 0; j < this.stepCode.length; j++) {
this.adimKodlari += this.stepCode[j] + ",";
}
this.adimKodlariLastString = this.adimKodlari.slice(0, -1);
console.log("inAxiosThen",this.adimKodlariLastString);
})
}
console.log("afterAxios",this.adimKodlariLastString);
return "apfapofapkapfka" --> It's working
return this.adimKodlariLastString --> It's not working. I want this to work.
},
In the solution examples I reviewed, the incoming value was used in the html tags. But I want to be able to use incoming value in methods.
When the string values I want to use are in .then(response), I can get them when I press the console.
When I press the console other than .then() I don't get the values.
You won't get any data this way because axios calls are asynchronous. The easiest and cleaner way is:
const response = await axios.get('url')
console.log(response.data)
this.adimKodlariLastString = response.data
// and then you could do your logic
Alternatively, to have it working with your code, you could just add:
await
before axios (you could do it with pure Promises but you'll end up getting Promise inside Promise).
Current,this is my function's last status. This is working. Return is successfull.
async getReasonsForWaitingCustomer() {
let adimKodlariLastString = "";
let stepCode = [];
let adimKodlari = "";
if (this.$route.params.status == "xxx") {
const response = await axios.get(URL + "xxx/xxx");
for (var i = 0; i < response.data.data.length; i++) {
if (stepCode.includes(response.data.data[i].adim_kodu) == false) {
stepCode.push(response.data.data[i].adim_kodu);
}
}
for (var j = 0; j < stepCode.length; j++) {
adimKodlari += stepCode[j] + ",";
}
adimKodlariLastString = adimKodlari.slice(0, -1);
}
console.log("adimKodu",adimKodlariLastString);
return adimKodlariLastString;
},
Promise in console
I looked at their solutions, but I didn't understand anything. In the solutions they manually assigned a value and called the result. I have no idea how to get my incoming data in PromiseResult.
Good news. I founded solution of this problem. I hope it will be useful for those who are looking for a solution. As I mentioned, I said that I wanted to use the value that came as a prompt in another function.
I'm successfully called my values in this function
I'm converted this function the async. I brought the first function as await.
Last result in the console
I have these two functions, where "form" is the name of the Vue object:
form.sizeChartAsImage();
form.setSizeChart();
This is the code of said functions:
setSizeChart: function () {
for (i = 0; i < this.columns.length; i++) {
this.product.size_chart.push({
position_x: 0,
position_y: i,
value: this.columns[i],
})
for (j = 0; j < this.data.length; j++) {
for (var key in this.data[j]) {
if(key === this.columns[i]) {
this.product.size_chart.push({
position_x: j+1,
position_y: i,
value: this.data[j][key],
})
}
}
}
}
}
sizeChartAsImage: function() {
html2canvas($("#size-chart").get(0)).then(canvas => {
canvas.toBlob (blob => {
var sizechartImg = document.createElement('img');
url = URL.createObjectURL(blob);
sizechartImg.src = url;
this.product.size_chart_image = sizechartImg;
debugger;
}, 'image/png');
})
}
Nevertheless, the second function get executed first (debugger enters first) and then the rest of the code runs; the form is submitted, and lastly, sizeChartAsImage() gets executed, causing no effect (since the form was submitted with "size_chart_image" as null)
This is what I'm trying to render, and it does generate the image.
<demo-grid
:data="data"
:columns="columns"
id="size-chart">
</demo-grid>
Could it be because it's a Vue component? Performance issues? Or do I need to use a callback maybe?
It's because html2canvas($("#size-chart").get(0)) is returning a promise (maybe just a thenable), which is an async call.
So sizeChartAsImage will run, and html2canvas($("#size-chart").get(0)) will execute. While the script is waiting for that to return it'll continue to setSizeChart function and run it. And then it'll return to the code within the then(canvas => callback.
You could either call setSizeChart at the end of the callback. Or, if you're using ES2017 or greater, you could re-write sizeChartAsImage to be async and await it.
I have an array of images as this.
var newImageParams = [
{
image_string: "hello",
_type: "NORMAL"
},
{
image_string: "hello",
_type: "NORMAL"
}
]
I am using mongo db and In nodejs, I am using mongoose, Everything is fine, I Can add data to mongoose table, but after adding each image, I am trying to save the resulting object into an array, Which in console.log is happening and right as well, but when I return that array, there is nothing in it and it empty
if (newImageParams) {
for (var i = newImageParams.length - 1; i >= 0; i--) {
// allImages[i]
var newImage = new Image(Object.assign(newImageParams[i], {bike: bike._id}));
newImage.save(function(err, image){
console.log(image);
allImages.push(image);
console.log(allImages);
});
}
}
This is what I am doing, everything is right and good in console but when I finally return,
res.json({bike: bike, images: allImages });
Images array is totally empty as it was when declared? why this is happening, please help
I bet you send response before your callbacks are done.
The easy and fast fix:
if (newImageParams) {
for (var i = newImageParams.length - 1; i >= 0; i--) {
const image = Object.assign(newImageParams[i], {bike: bike._id});
var newImage = new Image(image);
allImages.push(image);
newImage.save(function(err, image){
console.log(image);
console.log(allImages);
});
}
}
I will add correct approach with async/await, the older versions would use Promise.all or async.parallel:
async function doJob() {
if (newImageParams) {
for (var i = newImageParams.length - 1; i >= 0; i--) {
const image = Object.assign(newImageParams[i], {bike: bike._id});
const newImage = new Image(image);
await newImage.save();
allImages.push(image);
}
}
}
allImages.push(image); -> that line of code is async. It's will go to event queue and your console log will call immediately.
So console.log(allImages); will return empty.
You can read more in here. It will help you in future.
I have a function examples(a,b,c,…). Each argument can throw an error. All of them are handled the same way. I guess examples([array]) would fall in the same category.
In my code I have something like that currently:
for (i = 0; i < len; i++) {
try { this.example(arg[i]) }
catch (e) { log(e) }
}
From an user perspective Id like to be able to see the errors of all arguments at once instead of fixing one and then discovering the next one etc. But I end up catching myself which seems, to me, not desirable for an utility function.
Is there a way to rethrow all errors at once?
What are the best practices?
If there's a standard, why is it being recommended?
Well you can throw pretty much anything, but rather than just a simple Array, you may find you have more maneuverability using a custom error type
function ErrorCollection(msg) {
this.name = 'ErrorCollection';
this.message = msg || '';
this.errors = [];
}
ErrorCollection.prototype = Object.create(Error.prototype);
ErrorCollection.prototype.constructor = ErrorCollection;
ErrorCollection.prototype.push = function (e) {
return this.errors.push(e);
}
// ...
var i, ec = new ErrorCollection();
for (i = 0; i < 10; ++i) {
try {
throw new Error('Error ' + i);
} catch (e) {
ec.push(e);
}
}
// do something with ec, e.g.
if (ec.errors.length) {
console.log(ec, ec.errors);
throw ec;
}
An example of stopping on the first error then validity checking the remainder
var i, a = [];
for (i = 0; i < arguments.length; ++i) {
try { // assume everything will work
this.example(arguments[i]);
} catch (e) { // our assumption was wrong
a.push({arg: i, val: arguments[i]});
for (++i; i < arguments.length; ++i) { // loop from where we left off
if (!this.valid_arg(arguments[i])) { // some costly test
a.push({arg: i, val: arguments[i]});
}
}
throw a; // throw the list of bad args up a level
}
}
If the validity test will be fast/isn't costly then you may consider doing it in advance of your main loop instead of waiting for the first error, as you should then be able to avoid try..catch at this level entirely.
This must be a standard Javascript problem, but my searches here did not turn up any results that were applicable to me.
I am querying a mongodb database and want to add key/values to the objects in the result array through the enrichJSON function.
The MongoBooks.find returns some documents in an Array docs. Probably enrichJSON is a bad name, it should be something like enrichArray, it is arbitrary anyway.
function enrichJSON(jsonobj) {
for (var i = 0; i < jsonobj.length; i++) {
jsonobj[i].myparam = "something meaningful";
}
return jsonobj;
}
MongoBooks.find(mongoquery, function (err, docs) {
if (err) throw err;
var step1 = docs;
var step2 = enrichJSON(step1);
console.log("step1:" + step1);
console.log("step2:" + step2);
});
The step2 console output is missing the myparam output. Same goes if I try
jsonobj[i]["myparam"] = "abctest";
in the enrichJSON.
I am getting
step1: {[{"title":"good book"}]}
step2: {[{"title":"good book"}]}
but would like to get
step2: {[{"title":"good book", "myparam":"something meaningful"}]}
Edit to give an actual (stripped down) example of my result (I edited to make it simpler but probably mixed up the brackets):
step2:{ _id: 5474e8a35e79556ced436700,
isbn10: '0370303466',
author: 'Graham Greene',
lang: 'eng',
title: 'Travels with my aunt'
}
I am still missing what I added (myparam).
You have a broken response.
{[{"title":"good book"}]}
should be something like this :
{'data' : [{"title":"good book"}]}
Then your enrich function would look like this :
function enrichJSON(jsonobj) {
for (var i = 0; i < jsonobj.data.length; i++) {
jsonobj.data[i].myparam = "something meaningful";
}
return jsonobj;
}