How does render() actually execute? - javascript

I'm working on a project and I have created an interesting challenge for myself.
render() {
const sysObjs = this.state.systemObjects;
const jsonPackage = sysObjs[0];
if(typeof jsonPackage === 'undefined'){
console.log("undefined!!!")
} else {
//let jsonData = JSON.parse(jsonPackage.replaceAll("\\\\", "\\"));
console.log(jsonPackage.replaceAll("\\\\", "\\"));
}
console.log(jsonPackage.replaceAll("\\\\", "\\"));
If I run that it will fail and it will fail on the last line of the snippet and it will fail because from the perspective of that line of code jsonPackage is undefined and we get a "cannot read property 'replaceAll' of undefined. That makes sense to me as doing any sort of operation on something that is undefined would fairly difficult. The part that is confusing me though is that if I remove that last line then the code will execute. That's confusing to me because that console.log is also there within the if-else block so I would imagine that both should throw errors but they don't. In fact if I run that after removing the last line I will actually get both sections of the if-else block to log to the console and that tells me that this is executing in a way that I don't understand. Can anyone explain what's happening for me? At the root here something is doing something such that the const jsonPackage is not populated immediately when it is set so I'm curious how react actually handles something like that.
Lastly, I commented out that line because that line throws "Unhandled Rejection(Error) A cross-origin error was thrown." I'm also not too sure what that actually means either as it wasn't covered in the course I took.

render() is called each time your component's state updates. It can also get called in a number of other circumstances, but I think that's the relevant one for this situation.
You're not showing all of your code, but there is probably something running on mount or something similar that updates this.state.systemObjects.
On the first render of the component, this.state.systemObjects is undefined. So, if you have your console.log(jsonPackage.replaceAll("\\\\", "\\")); line in at the end of your render, it will fail because it can't do an operation on undefined. However, with it commented out, it runs fine, because it goes through the if(typeof jsonPackage === 'undefined'){ section of your if clause and not the else clause, where an operation is done on jsonPackage.
Then, once systemObjects is updated, render runs again. On that pass, systemObjects now is not undefined, so the else clause gets run -- since you don't have an undefined value any more, that else clause runs fine.
Your cross-origin error is not caused by any code you've shown -- it's probably caused by an API request somewhere. There are plenty of answers on SO about what can cause those.

I think you answered your own question. This is happening because the first time your code is executing, jsonPackage is not defined. This is why both blocks of your if statement are logging, because during the first pass though it is undefined and during the second it is, which will cause the else block to fire.
And this is why your code is breaking, during the first pass through of render, jsonPackage is not defined which will throw an error.
Its not really possible to explain why it's not defined the first time around without seeing the rest of your code. As a rule of thumb however, you should generally never assume that a React state value will always be defined and add checks accordingly.

Related

Uglify.js messing things up within if statement

I have this code
if (!win._hb_bids) {
win._hb_bids = {};
}
win._hb_bids.hb_auction_id = helpers.generateCacheBuster();
if (config.isAmazon) {
translated with Uglify.js to this code
if ((p._hb_bids = p._hb_bids || {}) && (p._hb_bids.hb_auction_id = e.default.generateCacheBuster()), s.isAmazon)
I would assume that this code should be executed from left to right, setting _hb_bids property to an empty object. Instead, what I see in the Chrome debugger, generateCacheBuster() is executed first, and then it tries to make an assignment to p._hb_bids.hb_auction_id, and p._hb_bids is undefined at this moment.
What is the reason for such a strange execution order? And how the code can be rewritten to work correcly after uglify?
UPDATE:
I found the cause of the issue. I had another part of the code doing iframe content refresh, and the win variable was pointing to an iframe window which was destroyed already. That probably caused this weird behaviour. So it wasn't uglify.js problem as I initially thought and stated in the title.

Able to Get Value in Console, But Undefined When Called From Function in Angular 2 App

I am running into an issue in my Angular 2 app where I'm getting an undefined error that's not making sense to me. What's more perplexing is that, after getting the undefined error, I can effectively check the exact same value in the console and get it with no issues.
First off, here's a version of the function that DOES work:
public getApplicableResult()
{
if (!this.customer || !this.customer.services || !this.customer.services.getAt(0))
{
console.log('Services not available...');
return;
}
else if (this.customer && this.customer.services && this.customer.services.getAt(0))
{
console.log(this.customer.services.getAt(0));
}
When I run this function I get this in the console for my "console.dir" of my object literal:
CustomerServiceDetails
assignments:(...)
authorizations:(...)
If I then click on "assignments" in the console, I get this expanded result:
assignments:
CustomerAssignmentCollection
count:1
items:Array(1)
So all the data is seemingly there and available.
However, if, in my function, if I were to try and access these nested values more directly - such as the value for "count" above, I get an undefined error. That's what I'm not understanding. Perhaps it's something I misunderstand about how the console works. But if seems to me that if I can access the value for this property in the console, then I should be able to access it directly via my function.
Here's an example of the same function, slightly altered to get the result for "count" within assignments, that returns undefined:
public getApplicableResult()
{
if (!this.customer || !this.customer.services || !this.customer.services.getAt(0).assignments)
{
console.log('Services not available...');
return;
}
else if (this.customer && this.customer.services && this.customer.services.getAt(0).assignments)
{
console.dir(this.customer.services.getAt(0).assignments.count);
}
The specific error I get is:
Cannot read property 'assignments' of undefined;
To add some additional info, assignments is not an array, it's a collection.
Two questions here:
1.) Why isn't my first "if" clause handling the 'undefined' error? How could I adjust it to handle the situation better?
2.) Why can I call the first function, get a result, and then drill down from there in the console to get what I need, but if I try and access a more nested part of the object in the function itself, I get undefined as a result?
One additional detail, I was calling this via Angular's ngAfterViewChecked(). So what that should do, even if it's a timing issue, is return the first "if" else clause result until the value is available, and then it should show the result - i.e., it should execute the "else if" block - because AfterViewChecked() keeps checking. But this isn't happening. I'm just getting the undefined result I mentioned above.

JS jumping code lines and loosing variable references

I'm having a couple of really weird issues with JS, I don't know if they are related but they both look like coming from a same code execution strangeness:
This is the first one, which almost got me mad since I could't find any explanation for why it was happening and had to totally rewrite the code to make this issue disappear, inside an express app:
exports.someMiddleware(req, res) {
var something = req.somethingFromPastMiddleware
something = doSomeComputation(something) //all synchronous
console.log(something, something.someProp) //everything ok
if(something.someProp) {
//here is where I must throw the error
console.log(something, something.someProp) //undefined; error, cannot read someProp of undefined (wtf?????)
something.otherProp = doSomeOtherComputation(something) //all synchronous
}
console.log(something, something.someProp) //everything ok
res.status(200).json(something) //I get the response, without something.otherProp
}
the only times I got something.otherProp in the response was when I purposely created an error inside the if(something.someProp) code block, like referencing an undeclared variable and catching catching it
and now the second one, inside a react web app we have a promise chain:
api.dataConfirm(
this.refs.code.value()
).then(() => {
data.refresh();
).then(() => {
this.returnToApp();
}).catch(err => {
console.error(err)
})
this code runs fine everywhere, except for the iOS webview on iOS 9.x running on an actual iPhone (iPods ok, iPads ok, emulator ok)
on the physical iPhone data.refresh() gets called and returns the promise but don't actually do anything, also here making JS to throw an error on purpose, catching it and then going further makes the code run in the normal way
refresh() {
return api.dataStatus()
.then(res => {
//here is where I must throw the error
this.cache(res.body.data);
});
}
I cannot really tell what's going on, maybe some code execution optimization that in some particular cases does this stuff and interrupting execution with an error and then resuming it makes things work fine... guesses?
PS: Another thing that make it work is doing some random coding before the lines that get jumped, like creating vars, summing numbers, etc... but the error thing it's the only one that works for sure

string comparison in Chrome changing mid-function

I am getting a totally bizarre issue in Chrome v. 33 that looks as if the string comparison operator is broken. It only occurs with the developer tools closed. I have the following function:
function TabSelected(data) {
var tab, was_design;
this.data = data;
tab = this.data.tab;
was_design = tab === 'design';
if (this.data.tab === 'design') {
this.tab = 1;
} else {
this.tab = 2;
console.log('was_design');
console.log(was_design);
console.log('is_design');
console.log(tab === 'design');
}
}
Which I call like so:
new TabSelected({
tab: 'design'
});
I have a setInterval running that runs this code every 50 ms. Most of the time, the if statement picks the first code path, so nothing gets logged to the console. However, after about ~8 seconds, it goes down the else code path. When I open the developer tools afterwards (since the bug doesn't happen when they're closed), I see the following log output:
was_design (index):96624
false (index):96625
is_design (index):96626
true (index):96627
I am... confused by this. I've also tried logging the contents of tab, which is in fact 'design', and logging this, which is a new TabSelected instance.
Am I losing my mind? Is Chrome losing it's mind?
UPDATE: I was able to reproduce it in a simplified setting: http://jsfiddle.net/WBpLG/24/. I'm pretty sure this is a bug with Chrome and I've filed an issue, see answer below.
Make this change and the problem should go away:
if (this.data.tab === 'design') {
to
if (String(this.data.tab) === 'design') {
However, I can confirm that typeof this.data.tab === 'string' both before the if clause and during the else, so I think this is only a partial answer at best.
Alternatively, I can also clear the problem by adjusting NewElementButtonSectionOpened.prototype.previous_requirement on line 59440:
// Create a single instance of the requirement and store it in the closure.
var cachedReq = new TabSelected({ tab: 'design' });
// Now just return that one instance over and over again.
NewElementButtonSectionOpened.prototype.previous_requirement = function() {
// deleted line: return new TabSelected({ tab: 'design' });
return cachedReq;
};
While both of these solutions fix the problem on my machine, it is not clear to me why this works.
I am afraid to mention it, but at one point, I was also able to prevent the error from happening by adding a throw new Error("..."); line in your else block. In other words, changing something in the else block altered the behavior of the if check. My only clue here is that the length of the error message mattered. For a while there, I could clear the error or cause the error consistently by altering the length of an error message that would never be thrown. This is so bizarre that I must surely have been mistaken, and indeed, I can no longer replicate it.
However, this is an extremely large JavaScript file. Maybe there is something to that. Maybe it is just a ghost story. This problem is certainly quite creepy enough without it, but just in case somebody else sees something similar... You aren't alone.
I was able to create a simple reproduction case, so I've filed a bug with Chromium.
The necessary conditions seem to be: a setInterval or repeating setTimeout, an expensive computation in the body of the interval, a call to a new Object passing data that contains a string, and a string comparison.

A weird prototype loading thing with Yabble.js

I have a very odd problem that I have to assume is because of Yabble.js. I have never used Yabble.js before, and the only reason I am now is because it is a dependency of a library I'm using (Gamejs), but I would love to understand why this happens, and whether it is actually Yabble.js's fault, or possibly Gamejs's.
Here's a heavily compressed (and modified for genericness) version of my main.js:
var gamejs = require('gamejs');
...
function Character(/*lots of arguments*/) {
Character.superConstructor.apply(this, arguments);
this.somethingtomakeitaprototypeforthisexample = oneofthearguments;
}
gamejs.utils.objects.extend(Character, gamejs.sprite.Sprite);
Character.prototype.draw = function(display){
display.blit(this.animator.image, this.pos);
}
... /*Skipping most of the file, irrelevant to the problem*/
function main() {
maincharacter = new Character(/* appropriate number and types of arguments */);
... /*skipping the rest*/
}
gamejs.ready(main);
I have done enough debugging to know that it gets into the main function no problem and that the break occurs at the call to Character. Here is the error message (from Chrome's console):
Uncaught TypeError: undefined is not a function
main
_readyResources
I have determined that Character is the undefined function. However, if I define my ready function thusly:
gamejs.ready(function(){
console.log('Character:');
console.log(Character);
main();
});
the full contents of Character, as properly defined, prints out, but I still get the error in main. Thus, I know that Character is defined by the namespace before main is called.
Fun fact though: I do have a workaround. If I change the function prototype for main to:
function main(CharacterClass) {...};
then change the ready function to:
gamejs.ready(function(){ main(Character); });
and change the relevant line in main to:
var character = new CharacterClass(...);
it works fine. But this feels really hackish.
So my question is not how to make it work, since I have that already, but rather why it is a problem and how to make it work like it's supposed to.
Any thoughts?

Categories

Resources