how to initialize states of array with unknown length in reactJS? - node.js

I faced a weird problem, I found the answer, however I want to know the reason behind the scene.
I have a react component which sends request toward server and after getting the result back, set it to the states of the component.
class Featured extends React.Component{
constructor(props){
super(props);
this.state = {
data : ['', ''],
error: null,
}
}
componentDidMount(){
axios.get('/api/featured')
.then(res=>{
let dataClone = [2];
console.log('before');
res.data.map((dt,i)=>{
dataClone[i] = dt;
});
this.setState({
data : dataClone
})
})
.catch(err=>{this.setState({error: err}); errorHandling(err)});
}
render(){
if(!this.state.error){
return(
<div>data: {this.state.data[1].title}</div>
)
}
else {
return (<div className='text-center text-warning'>There is some error loading the featured</div>)
}
}
}
here's the data which sent back :
{
title : 'how to be a good programmer?',
subtitle: 'I was wondering how can i be a good programer after 10 years experience',
author : 'hamid mortazavi',
date : new Date().toLocaleDateString(),
},
{
title : 'Why are you alone?',
subtitle: 'this is going to be a long story from here, but bear with me...',
author : 'shahin ghasemi',
date : new Date().toLocaleDateString(),
}
This works correctly, however if I change the way I've done initialization for data state, it does not work.
for example this three ways does not work:
this.state = {
//1:
data : new Array(2),
//2:
data : [],
//3:
data : [2]
}
none of these works, and when I want to show this.state.data[1].title for example, it does not work, and throw me Uncaught TypeError: Cannot read property 'title' of undefined. What surprises me is there is no problem displaying this.state.data[0].title and it seems the problem is the index.
As I said now I found the solution but it takes me 1 hour to solve that.
I just want to know the reason and why ?

This is not a problem of React but a problem of array items being undefined.
In your first example where you don't see the error, you actually defined the value of the 2 items in the state array by doing this
data : ['', ''],
This way, the value of data[0] and data[1] always exist and unless set by data response from server, will always have at least a value of an empty string.
In your second example, your data object is just a blank array of either no items ([]), 1 item ([2]), or 2 items (new Array(2)). Even with new Array(2), your data object items doesn't have a value defined. Try running this in your console:
x = new Array(2)
x[0] // will return undefined
x[1] // will return undefined

In the case that is not working you are setting the initial state.data to an empty array ( or in the last case to an array with one number 2 in it). When you call this.state.data[1].title you are trying to access a property on undefined, which give` you a null point error.
In the case that is working for you, you are trying access the property title on an empty string, which will be undefined, but it doesn't error because the thing you're trying to find the title of (the empty string) is not undefined.

Related

How to Update only passed value in mongodb using mongoose?

I have built an API that updates records in MongoDB using mongoose, but currently what happening is if I am passing only 4 field values in the JSON file of postman and try to update then all the values are updating with value null except that 4 fields which I had passed in JSON so can anyone help me how I can pass dynamic field and value that update only passed values of collection not all the fields of collection.
Passes JSON :
{
"preferance_id" : "60fe9ba1766d10d65c64083c",
"is_active": true,
"price_blur":true,
"affiliate_commission":27,
"language_code": "en"
}
Update call which I have passed in node js
transaction.update(PreferencesMasterName,
{ _id: new mongoose.Types.ObjectId(preferance_id) }, {
subscriptin_vat : subscriptin_vat,
popular_normal : popular_normal,
popular_crawled : popular_crawled,
price_blur : price_blur,
blur_rule : blur_rule,
affiliate_commission : affiliate_commission,
red_lock : red_lock,
automatic_dummy_price : automatic_dummy_price,
...
is_active: is_active
})
I want to pass dynamic field and values here instead of this because due to this other values are set will null value. So, can anyone have an idea how to do this?
You can do something like this:
const data = res.body; // should be an object that needs to updated
transaction.update({_id: PreferanceMasterName._id}, data, {new: true }, ( error, obj ) => {
if( error ) {
console.error( JSON.stringify( error ) );
}
console.log( obj );
});
In certain cases new doesn't work, you can use : { returnOriginal: false };
for more details, you can check this thread there are multiple ways you can do this.
Please check update how to use it.

Avoid duplicate code when api might return array or single object

I'm working with an API to consult car debits. If the car has more than one debit, the API returns an array of debits. If it has only one, it returns a single debit object (not an array with one element).
The problem is that I have to duplicate all the deserialization of this response checking whether the attribute is an array or a single object.
const debits = []
if (car.debits.length > 0) {
car.debits.forEach((debit: any) => {
debits.push({
id: uuidv1(),
description: debit.description,
label: debit.label,
amount: parseInt(debit.amount, 10)
})
})
} else {
debits.push({
id: uuidv1(),
description: debit.description,
label: debit.label,
amount: parseInt(debit.amount, 10)
})
}
Is there any way to simplify this? I showed just a small example but the object is much larger and there are many other attributes that I have to do the same.
If you have control over the API, you should probably have it return an array with a single element. If not, at the start of the function just force it into an array.
car.debits = car.debits.length ? car.debits : [car.debits]
If car.debits.length is undefined, which means it is not an array, you create an array and put the object inside it

Vuex State of an Array of Array is undefined

I have a store on Vuex with a socket listener.
This listener add to the state messages an array of array.
export const store = new Vuex.Store({
state: {
messages: []
},
mutations: {
SOCKET_GET_MESSAGES: (state, data) => {
state.messages[data[0].recipient] = data[0].res
// Data[0].recipient = the id of the recipient
// Data[0].res is an object with a login and a message.
},
}
In my console I can see the structure is correct if I do:
console.log(this.$store.state.messages)
with this output:
[__ob__: Observer]
2: Array(5)
> 0: {login: "w", message: "ABCD", id: 65}
> 1: {login: "w", message: "Deux", id: 66}
> 2: {login: "w", message: "Quatre", id: 67}
> 3: {login: "w", message: "J'envoie au deux", id: 69}
> 4: {login: "w", message: "Test", id: 70}
length: 5
__proto__: Array(0)
length: 3
__ob__: Observer {value: Array(3), dep: Dep, vmCount: 0}
__proto__: Array
But if I ask a specific ID I get undefined in my console log.
For example I ask for my first user with a message :
console.log(this.$store.state.messages[2])
Do you know how to solve this issue ?
I read lot of stuff on stackoverflow and on vuex documentation but I don't find an answer.
Thank you in advance for your help.
You're modifying the array directly, without using any actual method to do so. Therefore Vue cannot pick up the change you've done. You need to either use push or some other Vue helpers like $set. Quoting some helper docs:
When you modify an Array by directly setting an index (e.g. arr[0] = val) or modifying its length property. Similarly, Vue.js cannot pickup these changes. Always modify arrays by using an Array instance method, or replacing it entirely. Vue provides a convenience method arr.$set(index, value) which is syntax sugar for arr.splice(index, 1, value).
Also, here's a list of all supported mutation methods (wrapped by Vue):
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
Thank you everyone,
After some research I want complete the answer of Andrey Popov.
Indeed if we use Vuex we need to use directly:
Vue.set(state.object, key, data)
The this.$set method is not available in Vuex.
It's now working with this method.

Updating Mongo DB entries by params

The goal of this program is to add a property called 'userResponse' to the problem objects that reflects the input from the user. The problem object in Mongo DB is held in an array of objects, called 'problems', that is held in an object called 'session':
{
"_id" : ObjectId("59df5ee7adb378237377dbb4"),
"updatedAt" : ISODate("2017-10-12T12:24:07.269Z"),
"createdAt" : ISODate("2017-10-12T12:24:07.269Z"),
"userId" : "59df5edbadb378237377dbb3",
"problems" : [
{
"correctResponse" : 23,
"problem" : "20 + 3",
"secondTerm" : 3,
"firstTerm" : 20
} ]
}
Here is the logic for the endpoint that I have been using:
router.patch( '/session/:sessionId/:index', passport.authenticate( 'jwt', { session: false } ), ( req, res ) => {
Session.findOne({_id: req.params.sessionId})
.then( (item)=>{
item.problems[req.params.index].userResponse = req.body.userResponse;
Session.update({_id: req.params.sessionId}, item).then( (updated)=>{
res.json(updated.problems[req.params.index]);
});
})
})
I looked at some other examples ( one two ) but they do not seem relavant since this is a patch to single elements of the array as identified by the params.
This approach works, in that it successfully updates the database with the new properties on the objects, but during the execution the server logs this error:
(node:10955) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property '0' of undefined
My understanding is that this means the object can not be found in the database at some point, but since the database does include the new properties it must be resolved later. Is that corrrect?
Additionally it seems brittle to have two chained promises with no catch statements...
Can anyone offer suggestions on how to improve this process? This logic does successfully update the database, with errors. Is there a better way?
Thank you for your time.
Well i can see multiple issues in the code that can cause your problem
1- From the structure of your database session document _id is of type ObjectId and when you try to find, you are finding by just the id which is probably string you will need to use ObjectId(req.params.sessionId)
2- from the error, i can tell that the value of item does not contain an array of problems ... so items.problems is undefined so undefined[0] is an error.
3- You don't need to find session document item twice to update the array, i updated the code to update the userResponse value in array by using a single update operation.
4- for a better error handling and code maintainability you need to handle promise rejection so you need to handle catch as well as you are doing with then
So from the above comments i can update your code to be
var mongoose = require('mongoose');
router.patch( '/session/:sessionId/:index', passport.authenticate( 'jwt', { session: false } ), ( req, res ) => {
var index = req.params.index;
Session.update({_id: mongoose.Types.ObjectId(req.params.sessionId) }, {$set : {"problems." + index + ".userResponse" : req.body.userResponse } )
.then( (updated)=>{
console.log(updated);
res.json(updated.problems[req.params.index]);
})
.catch((err) => {
console.log(err.message);
res.json({status: "error" , message:err.message});
});
})

Search error while using Datatable with huge data in NodeJS

I have around 5,00,000 records in my database. I'm using DataTables to build an admin panel to manage the records.
I have Node.js as backed with MongoDB.
I have used this library - https://www.npmjs.com/package/datatables-query
So far on page load I have successfully loaded results a shown in the below Image.
Whenever I type something in the search box, I get 500 error as shown in the screenshot.
What could be the problem here?
Is DataTable a good option for showing grid with huge amount of data or is there any better option considering Node.js, Express and MongoDB combo?
Here is my server side code.
app.post('/getUsersData',function(req, res) {
var Model = require('./models/user'),
datatablesQuery = require('datatables-query'),
params = req.body,
query = datatablesQuery(Model);
query.run(params).then(function (users) {
var data = JSON.stringify(users);
// var data = JSON.stringify(users);
res.end(data);
}, function (err) {
res.status(500).json(err);
});
});
I have a table in MongoDB named User with 3 columns
1) Name
2) Email
3) Password
$(document).ready(function() {
var table = $('#datatable').DataTable({
// dom: 'Bfrtip',
processing: true,
serverSide: true,
order: [[1, 'asc']],
"aoColumnDefs": [ { "sClass": "hide_me", "aTargets": [ 0 ], visible: false } ], // first column in visible columns array gets class "hide_me"
ajax: {
url: "/getUsersData",
type: 'POST',
dataSrc: "data"
},
columns: [
{ data : "_id"},
{ data : "name" },
{ data : "email" },
{ data : "password" },
],
responsive: true
});
});
I would probably create an index on the data and search that index rather than searching the data itself. If you do however create a full text index you need to have a combination off all your cols in your collection and mongo allows only 1 full text index per collection.
As for alternatives, you could look into AWS's Elastic Search (which works just fine with MongoDB) or Sphinx Index (based on PostreSQL)
Edit:
I know this answer doesn't actually answer the question in the slightest but
I fear that the 500 error is not a memory issue in the application but rather on the DB (mongo is not like SQL, so don't design your applications like you would in SQL).
Some reading material, if you plan on changing db structure
http://rachbelaid.com/postgres-full-text-search-is-good-enough/
https://about.gitlab.com/2016/03/18/fast-search-using-postgresql-trigram-indexes/
The key here is figuring out what exactly went wrong on the DB or server side layer. The error message that you are alerting out is a generic message from the datatables library which points you to the following documentation: https://datatables.net/manual/tech-notes/7
The documentation linked to above says that this error will occur anytime a non-200 response is returned from the server. In this case you are explicitly throwing a 500 error in the error handler/callback of the datatables query call, but that doesn't give you/us the information we need!
The next step is to look at the response body in the network tab of your browser's console, since you are serializing the error as json and sending it along with your 500 response, or to console.log out the err in the error handler/callback in your node code. This will get you an understanding of what is actually going wrong, and with that information we can more accurately prescribe a solution. Post an update with whatever you log out please!
here is solution i tried to fix this issue:
goto ur jquery.datatable.min.js and then comment out the error message and you are good to go.
function K(a, b, c, d) {
// c = "DataTables warning: " + (a ? "table id=" + a.sTableId + " - " : "") + c;
// d && (c += ". For more information about this error, please see http://datatables.net/tn/" + d);
// if (b) E.console && console.log && console.log(c); else if (b = n.ext, b = b.sErrMode || b.errMode, a && r(a, null, "error", [a, d, c]), "alert" == b) alert(c); else {
// if ("throw" == b) throw Error(c);
// "function" ==
// typeof b && b(a, d, c)
// }
}

Resources