I am trying to learn jest. I keep getting stuck with the same problems over and over again. For example,
TypeError: Cannot read property 'value' of undefined
This came from a chain of errors I cleared one at a time. In this line,
const languageName = _.findWhere(languages, { key: this.props.screenProps.user.get('language') }).value;
It started with user, get, and now value is undefined. The first 2 errors I added into my user object then passed the object as props when rendering the component. But when I add value to the user object it still says that
'value' of undefined
Test
const tree = renderer.create(
<ProfileScreen screenProps={{ user }} />
);
user object
module.exports = {
id: '0052C000000gFJrQAM',
name: 'Bob Barker',
language: 'en',
get: language => language,
value: 'English (US)',
};
Question
What is the proper way to add "value" to the object so it stops throwing the error and renders the component?
According to the documentation of Underscore.js
findWhere_.findWhere(list, properties)
Looks through the list and returns the first value that matches all of
the key-value pairs listed in properties.
If no match is found, or if list is empty, undefined will be returned.
In your case, your test is making the job because findWhere didn't find any matches so it return undefined and you're trying to access value on undefined variable.
What you should do is to check if findWhere found something before accessing to the result.
const language = _.findWhere(languages, { key: this.props.screenProps.user.get('language') });
const languageName = (language !== 'undefined') ? language.value() : 'No language found');
Or if really you are sure findWhere will always return a value you should modify your test for make findWhere never undefined. That's depend a lot of where come from your languages list.
Related
I'm using JavaScript/jQuery to fetch data as json via an ajax call. The json is then parsed to a JavaScript object. Let's call the object var.
Now I want to assign a value from the object to a variable:
let publicationDay = val["work-summary"][0]["publication-date"]["day"]["value"]
This works, as long this value exists. However, for some results, the object contains null not for ["value"] but for ["day"] (as shown below)
val["work-summary"][0]["publication-date"]["day"] // is null
In this case, let publicationDay = val["work-summary"][0]["publication-date"]["day"]["value"] will throw the following error:
Uncaught TypeError: val['work-summary'][0]['publication-date'].day is null
and the code execution is stopped. Sure, I can check whether val["work-summary"][0]["publication-date"]["day"] === null, but what would be the most elegant way to handle this case? Maybe, in the future even
val['work-summary'][0]['publication-date'] // is null
may occur and my check for null does not work.
I would recommend trying optional chaining.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Assuming that any of those values could be null:
let publicationDay = val?.["work-summary"]?.[0]?.["publication-date"]?.["day"]?.["value"]
It will set the variable to either day.value or undefined
Depends heavily on your context but here’s some examples.
const publicationDate = val['work-summary'][0]['publication-date'];
// usage
console.log(publicationDate && publicationDate.day && publicationDate.day.value); // very basic
console.log(publicationDate?.day?.value); // optional chaining, better if supported by your environment (should be)
console.log(get(publicationDate, “day.value”)); // lodash get()
I am fairly new to JavaScript and am going over some code. However there is one bit i am unsure about.
product = product !== null && product[0] !== null && product[0].id || "";
Where product is an array. Could someone please help me understand what this does. Any help would be much appreciated. Many thanks =)
One way to understand what this does it to run it and observe the result.
Here's a JSBin showing 3 examples - which produce a different outcome, depending on the initial value of product - https://jsbin.com/roruvecubi/edit?js,console
To further clarify this with an explanation...
It will attempt to evaluate that all the following premises are true at the same time and re-assign product to the value of id of the first object found (if these are found) or an empty string if otherwise.
product array is NOT null
AND
First element of product array is NOT null
AND
First element of product array is an object containing a truthy key-value pair with key id. I.e. First element could like this:
{
id: "someValue" // product[0].id would be a truthy value in this case
}
AND
If 3.0 is true - assign value of id. If 3.0 is NOT true (id: does not contain a truthy object, array, number, string, true - Google the precise definition of truthy), then just assign empty string "" and thus result will be product = "";
product !== null it checks if product is null if it is it will stop right here and not do the other calculations (this is practiced so you won't get undefined, in this case, hmm null)
product[0] !== null checks if null, so when .id you won't get an error can't find id of undefined / null
let usr = null
console.log(usr.id)
GIVES ERROR Uncaught TypeError: Cannot read property 'id' of null
enter code here
With a few words, these are some practices to check if the VARIABLE has unwanted values to stop the calculations right there and not get errors.
Some prefer to try catch v--
Problem
When using mongoose to find documents by a specific field, if the type of the field is different from the type of the queried value, then mongoose will attempt to cast the query value to the same type as the field.
However, if the value cannot be casted, then mongoose will throw a CastError.
This behavior can be seen in the following example, where mongoose will attempt to cast the string 'invalid object id' to an ObjectId when trying to query for Foo documents by the bar field:
const fooSchema = new mongoose.Schema({
bar: {
type: mongoose.Schema.Types.ObjectId,
}
});
const Foo = <any>mongoose.model<any>('Foo', fooSchema);
await Foo.findOne({ bar: 'invalid object id' });
UnhandledPromiseRejectionWarning: Unhandled promise rejection
(rejection id: 1): CastError: Cast to ObjectId failed for value
"invalid object id" at path "bar" for model "Foo"
(Similar error would occur if bar was some other types, such as Number or Boolean as well.)
However, I don't like this behaviour. I very much prefer that the returned document simply becomes null, without an error being thrown. After all, if the value in the query cannot be casted, then the document logically also cannot exist under the given query.
Now, I am aware that I can simply do a check before constructing the query to ensure that I don't pass in a type that can't be casted. However, I don't like this approach because it results in having to duplicate code. Quite often, the code to handle what happens when the type is invalid will be exactly the same as the code to handle what happens if the document does not exist.
Additionally, I don't want to disable type checking completely either. So changing the type of the field to Mixed would not be a solution, as I would like the type check to still be there when I am creating a new document.
Attempted Solutions
So I attempted to solve this issue by creating custom query helpers to check the type, and return null if it is invalid, or perform the query if it is valid.
fooSchema.query.byBar = function(id) {
if (!mongoose.Types.ObjectId.isValid(id)) {
return null;
}
return this.where('bar').equals(id);
};
Which I can then use with Foo.findOne().byBar('invalid object id').
The problem with this solution however, is that it is no longer chainable. This means that if I were to try something such as Foo.findOne().byBar('invalid object id').lean(), it will throw an error if the type check fails (but will work fine when it passes the type check):
UnhandledPromiseRejectionWarning: Unhandled promise rejection
(rejection id: 1): TypeError: Cannot read property 'lean' of null
Trying to use static methods instead will run into the same problem.
fooSchema.statics.findOneByBar = function(id: any) {
if (!mongoose.Types.ObjectId.isValid(id)) {
return null;
}
return this.findOne({ bar: id });
};
Question
So essentially, I want to be able to have the query return null if the type check fails, but still be chainable. I want the query to be chainable because I still want to be able to specify additional query conditions in the cases where the type is valid.
Now, if my attempted solution is on the right track, then what I need to do is instead of returning null directly if the type check fails, I return something from the chainable query builder api that will result in the query's final result to be null. I essentially need some sort of 1=0 query condition (however, the linked question did not propose a guaranteed answer for the 1=0 condition). So my question is, if my attempted solution is on the right track, then what do I return that is a chainable mongoose Query, but will cause the query to always result in null when the query is executed?
Or alternatively, if my attempted solution is on the wrong track, is there another way to make a mongoose find query result in null instead of throwing a CastError when the types are incompatible?
May be my answer is a bit silly and straight forward. But I would like to do it this way.
fooSchema.statics.findOneByBar = function(id: any) {
if (!mongoose.Types.ObjectId.isValid(id)) {
id = "111111111111111111111111"; // other values are also possible
}
return this.findOne({ bar: id });
};
This is a valid objectId and it may never be generated by Mongodb ever. So this way the query will be valid and all the chainable functionality will be intact. Let me know if it helps you.
Building off of the attempted solutions, I found two query conditions that seem to be functionally equivalent to 1=0.
1. Passing an empty array to the $in query condition.
fooSchema.query.byBar = function(id) {
if (!mongoose.Types.ObjectId.isValid(id)) {
return this.where('bar').in([]);
}
return this.where('bar').equals(id);
};
2. Using $where query condition that returns false.
fooSchema.query.byBar = function(id) {
if (!mongoose.Types.ObjectId.isValid(id)) {
return this.$where('false');
}
return this.where('bar').equals(id);
};
The advantages of these two solutions is that they work regardless of the type of bar and the possible values it may contain, as long as the type check is done correctly. That means we don't need to have prior knowledge of what values may or may not be stored in the fields, so arbitrary numbers and booleans are supported by these solutions as well.
The disadvantage of these two solutions is that such a query helper needs to be added for every field where we need to return null if the types don't match, and it needs to be repeated for every model. So we do not have the flexibility of the original mongoose query builder api anymore, as well as resulting in a lot of duplicate code.
Additionally, I'm not sure if either of these two conditions carry performance penalties.
If the flexibility of the mongoose query builder api is needed, a work around could be to use a try-catch statement, as it will catch the CastError that is thrown. Though it is a bit cumbersome to write.
let foo;
try {
foo = await Foo.findOne().where('bar').equals('invalid object id');
} catch (err) {
foo = null;
}
Though, if anyone have an even better solution, I am interested in hearing it.
I have a projects object like so
projects: {
projectType: {id: 1, title:'something'},
budgetType: {id: 1, title:'something'},
projectStatus: {id: 1, title: 'something'}
}
and im rendering this in the render method.
<td>{this.props.projects.projectType.title}</td>
<td>{this.props.projects.budgetType.title}</td>
<td>{this.props.projects.projectStatus.title}</td>
This works fine, but sometimes the server sends in null when that object is not present as it is not a required field to be entered. So, this throws a "cannot read property of null error". I was using a ternary operator in each case to solve this error which doesnt look really nice. Is there any better way to solve this?
<td>{(this.props.projects.projectType.title)?this.props.projects.projectType.title: ''}</td>
EDIT:
I have a "ProjectList" component which lists all the project rows like so
//in render
<tbody>
{Object.keys(this.props.projects).map(this.renderProject)}
</tbody>
//renderProject Function
<Project key={key} project={this.props.projects[key]}/>
When accessing properties of null Javascript will throw this error. One usual pattern we use is like:
this.props.projects.projectType && this.props.projects.projectType.title
Here the second expression is evaluated only if first one is true. null and undefined are false so the second one won't be evaluated, an no error thrown.
This is because false && <whatever> === false
If projectType is not null, the value of the expression will be equal to the last item in the chain.
This can be chained in fancy ways like:
this && this.props && this.props.projects && this.props.project.projectType;
But it is always recommended to keep these checks inside the javascript file and use some derived attribute for the view.
I don't know if ampersand is a valid token in react expressions. Please refer to other answers on how such cases are handled in React way.
Why not create a simple helper method which accepts the property and returns either the value or an empty string if its null? You would still do the ternary operator but only in one place
I am trying to access a certain member in a JavaScript object. In order to do this, I need to try out a couple of key values.
For example, Object['text/html'] which will give me an export link for a HTML document. However, not every object of this type will have a text/html key pair value.
In Python I would solve this problem using a Try-Catch block, with the KeyError exception. If I can do something similar in javascript, as in use an exception in a Try-Catch block, that would be great.
However, if alternatives exists instead of try catch blocks, that do achieve the same end goal, I would like to know about them as well.
EDIT:
I would prefer to use an exception over using functions. I do this because the text/html key might not be there, but it should be there. An exception seems more appropriate for this scenario
Javascript doesn't generate an exception when reading or writing a property that doesn't exist. When reading it, it just returns undefined. When writing it, it just creates the property.
You could create your own function that tests to see if the property exists and throws an exception if it does not (but you'd have to call that function whenever), but JS doesn't make an exception out of that on it's own like you are asking for.
If you want to test if a key exists on an object in javascript, you can use this construct with the in operator:
var obj = {};
var key = "test";
if (key in obj) {
// key exists
} else {
// key doesn't exist
}
If you try to read a key that doesn't exist, you will get undefined as the value.
var obj = {};
var value = obj.test;
alert(value === undefined);
The in operator does a better job of telling you whether the key exists that testing for undefined because undefined is a legal value for a key that exists.
In many cases, where you control the values that the keys have and a key that is present will never have a falsey value, you can also just check if the key has a truthy value:
var obj = {};
var obj.test = "hello";
if (obj.test) {
// key exists and has a truthy value
}
If you want to make sure that the object itself has the property and not any prototype that it is inheriting from, then you can do this:
var obj = {};
var obj.test = "hello";
if (obj.hasOwnProperty(test)) {
// key exists on the object itself (not only on the prototype)
}
Read this!
The accepted answer is correct however omits some points.
1) Accessing nested object
Like someone pointed out in the comment, Javascript returns undefined when the key doesn't exists in the object.
However, if you need to access an object inside an object (or an Array, or a function), well this break.
let a = {};
let userName = 'js'
let data = a.response[userName];
Cuz you will received actually a TypeError, basically because we are trying to read a property of undefined, which doesn't have any.
VM187:2 Uncaught TypeError: Cannot read properties of undefined (reading 'js')
at <anonymous>:2:22
2 Answering the question
The Python principle "Ask forgiveness not permission" - explain is actually for the most part working well in Javascript (and PHP, you didn't ask but well..). There are for sure some difference, or some situation where the difference is important, but for most use cases is the same
So this is how you would do it:
try {
let data = a.key1.key2['whatever'].nested.damn.object;
console.log(data)
} catch (error) {
let data = "noope";
console.log(data);
}
As you can see, in Javascript you don't really care about the error type, (for the most part, sure other situation you should case). Is almost like anything is in a Python's
try:
a = "hello" + 1 + {} + [] # crazy stuff here
except BaseException as bleh:
print(str(bleh))
Documentatin
MDN Working with objects
How do I check if an object has a key in JavaScript? [duplicate]