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
Related
My html code contains a <ul id="users"></ul>, which is then populated dynamically with JS code
const li = document.createElement("li");
li.appendChild(document.createTextNode(`${nameInput.value} : ${emailInput.value}`)
I added a button in the html code to delete all users in that <ul>,as such: <button class="btn" id="del-user-list" onClick="deleteUserList()">Delete User List</button>.
My deleteUserList function in the JS code looks like this:
function deleteUserList() {
while (userList.firstChild != "") {
userList.firstChild.remove();
}
}
This works on the surface, however I realize that after the last child, my function will check once again for the value of a child that doesn't exist. I remember from studying C and playing with linked lists that you don't want to dereference a pointer that points to null.
Sure enough, when I look at the console I get
Uncaught TypeError: Cannot read properties of null (reading 'remove') at deleteUserList (main.js:31:25) at HTMLButtonElement.onclick ((index):29:77)
Is this a problem and what can I do about it? I just started playing with Javascript and don't have a good sense of how those things work right now.
Instead of comparing userList.firstChild to an empty string, you should compare it against null or omit the comparison operator entirely:
while (userList.firstChild != null)
// or
while (userList.firstChild)
The latter one works because converting null to a boolean value returns false
null != '' will always be true because userList.firstChild will never be an empty string anyway. It will either be a DOM node or null.
I am trying to get the length of an object but am getting this error message:
Uncaught TypeError: Cannot read property 'length' of undefined
I am trying to get the length and getting a popup when the length is zero.
I tried using this.props.storyboardTargets.length === 0
case 1: data not available so (!this.props.storyboardTargets)--> undefined
case 2: data was there and later deletd or cleared so need to check the length
Here is my code below:
handleShowPopupTarget = () => {
if (this.props.storyboardTargets && !this.props.storyboardTargets.length) {
console.log(this.props.storyboardTargets);
toastWarning(WARNING_MSG_NO_TARGET);
}
};
The way you have it written now does not handle the issue that this.props.storyboardTargets may be undefined. You need to update it like so:
handleShowPopupTarget = () => {
if (!this.props.storyboardTargets || !this.props.storyboardTargets.length) {
console.log(this.props.storyboardTargets);
toastWarning(WARNING_MSG_NO_TARGET);
}
};
This means if storyboardTargets is undefined, or its length is 0, toastWarning will fire.
As an alternative, you could define a default prop for your component of an empty array for storyboardTargets. That way it would never be undefined.
The error message means that storyboardTargets in undefined. Try seeing if storyboardTargets is being passed in to the component that contains your handleShowPopupTarget method.
use lodash's get, it simplifies a lot those kind of buggy checks:
_.get(this.props, 'storyboardTargets.length', 'default'); // you could use 0 instead of 'default' in your case
Your original code was
if (!this.props.storyboardTargets.length) {
And it fails because this.props.storyboardTargets is undefined and well you can not read properties of something that is undefined so it throws an error.
So after that you listened to advice and changed your code to be
if (this.props.storyboardTargets && !this.props.storyboardTargets.length)
So now this stops the undefined error from happening on this line because the truthy check on this.props.storyboardTargets prevents the evaluation of the second half of the code. So that means the code will not go into the if. but you WANT it to go into the if statement if it is not defined.
So what you need to do is change it to be an OR check so if it is NOT defined OR it does not have a length
if (!this.props.storyboardTargets || !this.props.storyboardTargets.length)
Now it goes into the if statement id it is undefined and will not throw the error.
The other solution is to see that it is undefined and set it to a default value
this.props.storyboardTargets = this.props.storyboardTargets || []
if (!this.props.storyboardTargets.length)
Now if the array is undefined, it sets it to an empty array and the if check will work correctly. Changing the data might not be the best solution if other things rely on undefined.
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.
I have this method
var link = this.find_first_link(selectedElem);
which should return an object. I'm not sure what should it return if no element is found - null, undefined or false? I've rejected 'false' option since I don't think it suits here so I'm choosing betwen null or undefined. I've read that 'undefined' should be used where some kind of exception or error occurs, so currently this method returns null. Is that OK?
Look at what is done in methods you have in your browser.
getElementById returns null when there is no element with the id provided.
That's what null has been designed for : to represent the absence of an object (typeof null is "object"). Use it when the expected returned type is "object" but you have no object to return. It's better than undefined here because you would have undefined before you even decided what to put in the variable or before you even call the function.
From the description of null in the MDN :
In APIs, null is often retrieved in place where an object can be
expected but no object is relevant.
Yes, use null.
Yes,
var link = this.find_first_link(selectedElem);
It will return NULL.
I'm getting a nonsensical error message in IE8, tell me that the constant '2' is null or not an object. The line of code is:
if (! localtree[idx][2]) {
I also tried coding it like this:
if (localtree[idx][2] == 0) {
The value in the array at that location is always zero (for now).
How can IE8 think that the number 2 is null? I'm mystified!
The exact error is:
Message: '2' is null or not an object
Has anyone seen this?
EDIT : This is a very misleading error message. See my answer below for what actually went wrong.
This is a very confusing error message. It turned out that I was stepping one element beyond the end of the array. 'idx' was referencing a non-existent value that I was attempting to treat as an array reference (with the [2]).
Rather than telling me that '2' was null, it should have said that 'localtree[idx]' was null.
The root cause of this was that I had a trailing comma where I defined the array, leading to an extra, null value in the array. In firefox, trailing commas are ignored (like in perl), but in IE, they are significant.
the constant '2' is null or not an object
if (! localtree[idx][2]) {
JavaScript doesn't have constants, at least not yet. And you aren't checking a number, but a member of an array, i.e.: the variable with index number 2 of object localtree[idx] (where idx must contain a string to refer to an object property or an index number to refer to an array).