this.state is undefined when used in function React JS component [duplicate] - javascript

This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 6 years ago.
I have the React component below and I want to access this.state.board (a 2D array with 0 or 1 for each element) from a function randomPosition(). When I call randomPosition it returns "cannot read property state of undefined". Am I doing something wrong with the this keyword?
var App = React.createClass({
getInitialState(){
return {
board: []
}
},
randomPosition: function(){
//generates a random position on this.state.board array
var position = [];
var positionX = null;
var positionY = null;
var generatePosition = function(){
positionX = Math.floor(Math.random() * 64);
positionY = Math.floor(Math.random() * 64);
if(this.state.board[positionX][positionY] === 1){
position.push(positionX, positionY);
return position;
} else {
generatePosition();
}
}
generatePosition();
}
})
thanks for the help!

Your generatePosition function will have its own scope and hence this inside this function will point to its own scope rather than the outer scope where state is accessible. You can store the reference of the outer scope and then use that inside this function like
randomPosition: function(){
//generates a random position on this.state.board array
var position = [];
var positionX = null;
var positionY = null;
var that = this;
var generatePosition = function(){
positionX = Math.floor(Math.random() * 64);
positionY = Math.floor(Math.random() * 64);
if(that.state.board[positionX][positionY] === 1){
position.push(positionX, positionY);
return position;
} else {
generatePosition();
}
}

Related

The function `this` doesn't work in module of nodejs

I create a module with following
module.exports = {
GetRandomNum:(Min,Max)=>{
var Range = Max - Min;
var Rand = Math.random();
return(Min + Math.round(Rand * Range));
},
mathCalculationtion:()=>{
var firstPlace = this.GetRandomNum(1, 9);
return firstPlace;
}
}
I run this above code and get an error at the line var firstPlace = this.GetRandomNum(1, 9);
at Object. mathCalculationtion (/home/sfud/projectland/lib/comlib.js)
Please help me, thank you.
You are using arrow functions. The this variable does exist within regular objects, but arrow functions pull their this from whatever this is when they're declared (unless you bind them, which would be an odd thing to do).
Change your functions to functions and it should work fine.
module.exports = {
GetRandomNum(Min,Max) {
var Range = Max - Min;
var Rand = Math.random();
return(Min + Math.round(Rand * Range));
},
mathCalculationtion() {
var firstPlace = this.GetRandomNum(1, 9);
return firstPlace;
}
}
Note: To use it this way, you will need to import the module and call the function with the . syntax.
// This will work
const myModule = require('./my-module');
console.log(myModule.mathCalculationtion());
// This will not work
const { mathCalculationtion } = require('./my-module');
console.log(mathCalculationtion());
This is because this within the function is whatever the x in x.myFunc() is. If you just call myFunc() directly, it has no idea which object to apply it to. If you want to get around this, either define your functions in your module separately and reference them by name in the module, then export each function, or you can use .bind().
Change this.GetRandomNum(1, 9) to module.exports.GetRandomNum(1, 9) or
declare your functions outside of the module.exports block:
var getRandomNum = (Min,Max) => {
var Range = Max - Min;
var Rand = Math.random();
return(Min + Math.round(Rand * Range));
}
var mathCalculationtion = () => {
var firstPlace = getRandomNum(1, 9);
return firstPlace;
}
then:
module.exports = {
getRandomNum,
mathCalculationtion
}
Use module.exports instead of this:
module.exports = {
GetRandomNum(Min,Max) {
var Range = Max - Min;
var Rand = Math.random();
return(Min + Math.round(Rand * Range));
},
mathCalculationtion() {
var firstPlace = module.exports.GetRandomNum(1, 9);
return firstPlace;
}
}
It works for me just fine in NodeJs v12.16.1.

Javascript setInterval time not working [duplicate]

This question already has answers here:
Changing the interval of SetInterval while it's running
(17 answers)
Closed 5 years ago.
I can't seem to figure out why the farmerTime is not updating when you level up. There is a button that just adds a level to farmingLevel.
window.setInterval(function() {
farmerTime = 2500;
farmerLevel = 3;
x = farmerTime;
y = farmerLevel;
z = x / y;
farmerTime = z;
if (farmers >= 1) {
a = farmers;
b = potatoes;
c = a * 1;
d = b + c;
potatoes = d;
}
}, farmerTime);`
You need to define farmerTime before you use it. In your case, before the setInterval function. Also, if you want to change the farmerLevel you need to change it somewhere else, not in the setinterval function.
Changing level example:
<button type="button" onclick="setFarmerLevel(farmerLevel + 1);">Change level </button>
And the code for the interval thing:
var farmerTime = 2500;
var farmerLevel = 1;
var setFarmerLevel = function (level) {
farmerLevel = !level ? 1 : level;
farmerTime = farmerTime / farmerLevel;
clearInterval(farmerInterval);
farmerInterval = window.setInterval(run, farmerTime);
};
var run = function () {
if (farmers >= 1) {
a = farmers;
b = potatoes;
c = a * 1;
d = b + c;
potatoes = d;
}
};
var farmerInterval = window.setInterval(run, farmerTime);
UPDATE
I forgot setInterval's function time cannot be change in runtime, so the code is updated now.

When you use JavaScript functions as classes, do they still get parsed in a top-down fashion?

I'm trying to figure out why my Google Chrome console is giving me the error "undefined is not a function." I have a hunch, but maybe I'm on the wrong track. My function boxCollision(...) is defined at the bottom of my class. Nearer to the top I have a statement
if (this.boxCollision(this.food.getBBox(), this.body[0].getBBox()))
this.food.translate(this.linkSize, 0);
the first line of which is causing the error I mentioned. I think that's maybe because I haven't yet defined boxCollision, so it's essentially nonexistent. Is that right? The getBBox() functions are recognized because they're from an external JavaScript file.
function snakegame(C, C_w, C_h, spd)
{
/* NOTE TO SELF: C is a Raphel object. Can't find a method to return the height
and width of a Raphael object in the documentation:
http://raphaeljs.com/reference.html#Raphael.
Using C_h and C_w for now, but should probably change it later.
*/
this.linkSize = 50; /* size of a snake unit, in pixels; must divide C_h and C_w */
this.link = C.rect(C_h/2, C_w/2, this.linkSize, this.linkSize);
this.link.attr("fill", "#E9E581");
this.body = [this.link];
this.food = C.rect(randInt(0,C_w/this.linkSize-1) * this.linkSize, randInt(0,C_h/this.linkSize-1) * this.linkSize, this.linkSize, this.linkSize);
if (this.boxCollision(this.food.getBBox(), this.body[0].getBBox()))
this.food.translate(this.linkSize, 0);
this.food.attr("fill","#B43535");
this.maxSnakeSize = C_h * C_w / (this.linkSize * this.linkSize);
/* On instantiation, the snake direction is down and has 1 link */
this.dy = 0;
this.dx = 0;
this.score = 0;
/* Event listener for changing the direction of the
snake with arroy keys on the keyboard
*/
this.redirect = function(dirnum)
{
switch (dirnum)
{
/*
dirnum corresponds to
1 ---> right
2 ---> down
3 ---> left
4 ---> up
*/
case 1:
this.dx = this.linkSize;
this.dy = 0;
break;
case 2:
this.dx = 0;
this.dy = this.linkSize;
break;
case 3:
this.dx = -this.linkSize;
this.dy = 0;
break;
case 4:
this.dx = 0;
this.dy = -this.linkSize;
break;
default: /* never happens */
break;
}
}
this.move = function()
{
if (this.body.length == this.maxSnakeSize)
{
this.destruct();
return;
}
var addLink = false;
var BBhead = this.body[0].getBBox();
if (this.hitWall(BBhead) || this.hitSnake(BBhead))
{
document.getElementById("snakescorediv").innerHTML = "<p>GAME OVER!</p><p>Score: "+ this.score +"</p>";
this.destruct();
return;
}
var BBfood = this.food.getBBox();
if (this.boxCollision(BBhead, BBfood))
{
this.moveFood();
this.score += 10;
document.getElementById("snakescorediv").innerHTML = this.score.toString();
addLink = true;
}
if (addLink)
this.body.push(this.body[this.body.length - 1].clone());
for (var i = this.body.length - 1; i > 0; --i)
{
var prevBB = this.body[i-1].getBBox();
var thisBB = this.body[i].getBBox();
this.body[i].translate(prevBB.x-thisBB.x, prevBB.y-thisBB.y)
}
this.body[0].translate(this.dx, this.dy);
}
this.mover = setInterval(this.move.bind(this), spd);
this.hitWall = function(bb)
{
return bb.x < 0 || bb.x2 > C_w || bb.y < 0 || bb.y2 > C_h;
}
this.hitSnake = function(bb)
{
var retval = false;
for (var i = 1, j = this.body.length; i < j; ++i)
{
var thisbb = this.body[i].getBBox();
if (this.boxCollision(bb, thisbb))
{
retval = true;
break;
}
}
return retval;
}
this.moveFood = function()
{
var bbf = this.food.getBBox(); // bounding box for food
do {
/* tx, ty: random translation units */
tx = randInt(0, C_w / this.linkSize - 1) * this.linkSize - bbf.x;
ty = randInt(0, C_h / this.linkSize - 1) * this.linkSize - bbf.y;
// translate copy of food
this.food.translate(tx, ty);
bbf = this.food.getBBox(); // update bbf
} while (this.hitSnake(bbf));
}
this.boxCollision = function(A, B)
{
return A.x == B.x && A.y == B.y;
}
this.destruct = function()
{
clearInterval(this.mover);
for (var i = 0, j = this.body.length; i < j; ++i)
{
this.body[i].removeData();
this.body[i].remove();
}
this.food.removeData();
this.food.remove();
this.score = 0;
}
}
Put the methods on the prototype to avoid this issue.
This won't work:
function Ctor() {
this.init()
this.init = function() {
console.log('init')
}
}
var inst = new Ctor // Error: undefined is not a function
But this will:
function Ctor() {
this.init()
}
Ctor.prototype.init = function() {
console.log('init')
}
var inst = new Ctor // init
Javascript parses code in two steps: compilation and evaluation.
The first step is compilation. In this step all definitions are compiled but no statement or expressions are evaluated. What this means is that definitions such as:
function a () {}
and:
var x
gets compiled into memory.
In the evaluation phase the javascript interpreter finally starts executing. This allows it to process operators which makes it possible to execute statements and expressions. It is in this step that variables get their values:
var x = 10;
^ ^
| |______ this part now gets assigned to `x` in the evaluation phase
|
this part was processed in the compilation phase
What this means is that for function expressions:
var x = function () {}
while both the variable and function body are compiled in the compilation phase, the anonymous function is not assigned to the variable until the evaluation phase. That's because the = operator is only executed in the evaluation phase (during the compilation phase all variables are allocated memory and assigned the value undefined).
Both the compilation phase and evaluation phase happen strictly top-down.
What some call "hoisting" is simply the fact that the compilation phase happen before the evaluation phase.
One work-around is to simply use a function definition instead of a function expression. Javascript support inner functions so a function defined in another function doesn't exist in the global scope:
function boxCollision (A, B) {
return A.x == B.x && A.y == B.y;
}
this.boxCollision = boxCollision;
Then you can use it at the top of your constructor:
if (boxCollision(this.food.getBBox(), this.body[0].getBBox()))
this.food.translate(this.linkSize, 0);
Note that you can't use this.boxCollision because it's still undefined when you call it.
Another obvious work-around is to of course assign this.boxCollision = function (){} at the top before using it.
Or you could even assign it to the constructor's prototype. Or you can have an init function that gets called at the top (note: function, not method - again the use of a definition instead of a function expression make use of "hoisting").
There are many ways to get around this. But it's useful to know why it's happening to understand what works and what doesn't.
See my answer to this related question for more examples of this behavior: JavaScript function declaration and evaluation order

Mouseover on all tiles [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I'm trying to make a hover for every tile, but when I use tileArray[i].x it uses the last tiles position. And I'm trying to get the position of the tile I'm hovering.
Here is the code I've made.
for (x = 0; x < mapxtiles; x++) {
for (y = 0; y < mapytiles; y++) {
if(map[x][y].height != 'x') {
i++;
var topPos = (x * 16) + (y * 16) - 24;
var leftPos = (y * 32) - (x * 32) + (mapxtiles * 32) - 32;
var normalTileTexture = PIXI.Texture.fromImage("./assets/map/normal.png");
var tileHoverTexture = PIXI.Texture.fromImage("./assets/map/hoverTexture.png");
tileArray[i] = new PIXI.Sprite(normalTileTexture);
tileArray[i].setInteractive(true);
var tileHover = new PIXI.Sprite(tileHoverTexture);
tileArray[i].mouseover = function(mouseData) {
tileHover.position = new PIXI.Point(tileArray[i].x - 2, tileArray[i].y + 22);
floorMap.addChild(tileHover);
};
tileArray[i].position = new PIXI.Point(leftPos, topPos);
floorMap.addChild(tileArray[i]);
}
}
}
i is a counter which has reached a certain value at the end of your loop. if you hover your tile, it will always have the last value. a workaround for that, is to wrap your code in a closure:
(function (a) {
tileArray[a].mouseover = function(mouseData) {
tileHover.position = new PIXI.Point(tileArray[a].x - 2, tileArray[a].y + 22);
floorMap.addChild(tileHover);
};
})(i);
what i do here:
i wrap your event-handler in an iife with i as parameter and recieve it as a inside the closure. this is for illustration purposes, you could of course leave the inner var by the name of i
it is also a little bit more readable, to just move it into a function which is declared outside of your loop:
function helperfunction (tileArrayElement, tileHover, floorMap) {
tileArrayElement.mouseover = function(mouseData) {
tileHover.position = new PIXI.Point(tileArrayElement.x - 2, tileArrayElement.y + 22);
floorMap.addChild(tileHover);
};
}
and call it in your loop:
for (x = 0; x < mapxtiles; x++) {
for (y = 0; y < mapytiles; y++) {
if(map[x][y].height != 'x') {
// your other code...
helperfunction(tileArray[i], tileHover, floorMap);
// your other code...
}
}
}

why is this javascript array undefined?

I have the following code, which attempts to generate a 2 dimensional array of random numbers:
var block_size = 32;
var can_width = can.width;
var color_depth = 12;
var passes = can_width / block_size;
var map_store = new Array(passes);
for(i=0;i<passes;i++) {
for(j=0;j<passes;j++) {
map_store[i] = new Array(passes);
color = Math.random() * color_depth;
map_store[i][j] = Math.round(color);
}
}
which seems to work fine if i put console.log statements within the loop, however if I try to access the map_store array outside of the loops. all of it's elements are undefined. why is this?
map_store[i] = new Array(passes); should be above the 2nd for loop. You're clearing your previous j values.
for(i=0;i<passes;i++) {
map_store[i] = new Array(passes); // <--
for(j=0;j<passes;j++) {
color = Math.random() * color_depth;
map_store[i][j] = Math.round(color);
}
}

Categories

Resources