How to pass an Event Object into a nested function? - javascript

How do I pass an event object into a nested function and have it be understood by the script?
Below is not my actual code but it illustrates the issue. The function there cannot complete, because evt.target.src is out of scope, I guess. Again, this is dumbed down code. I'm only asking how can the evt.target.src be understood from inside this nested function.
SomeFunction =(evt)=> {
setTimeout(
()=> {evt.target.src = somethingIcreated}, 200
)
};
If I assign it to a variable outside of the nested function to pass in, it's just understood as some random variable, not the actual event object's src. Thus the src won't change or the project will crash because it cannot work with something that is null.

React uses synthetic events and it reuses them, so you're not allowed to use them after the handler returns.
You have two choices:
You can grab properties from them and them later. So in your case:
SomeFunction = ({target}) => {
setTimeout(
()=> {target.src = somethingIcreated}, 200
)
};
There, I'm using destructuring in the parameter list to grab the target property from the event object, which it's okay to reuse later. It's just the event object itself that you can't use after the handler returns.
You can use the persist method on the event to tell React you want to keep it.
More in the synthetic events documentation.

Related

In this context, what exactly does $('id').on('click', this.method.bind(this)) do?

Here is the app I'm referring to:
I am trying to fundamentally understand the bind method in Javascript.
My understanding when I play around with it in the console is that bind returns a copy of the function, with "this" bound to whatever you pass into bind.
function logThis(){
console.log(this)
}
logThis.bind({today: 'Tuesday'})
//Will return a copy of the logThis function, with 'this' set to the
{today:'Tuesday'} object. The code does not run right away though.
var explicitlyLogThis = logThis.bind({today: 'Tuesday'});
explicitlyLogThis(); //This will run the code and display the {today: 'Tuesday'} object to the console.
This is my understanding so far. I understand that to actually run this new function that has 'this' explicitly bound using the bind method, you need to set it to a variable and then run it.
I see a contradiction when I look at the app in the above link. If you look at the bindEvents method on line 56, we have .on('keyup', this.create.bind(this)). I understand that we have to set 'this' to App when we run the create method because jQuery defaults to setting 'this' to the jQuery object itself. So this line is actually the same as: $('#new-todo').on('keyup', App.create.bind(App)).
That isn't where my confusion is. My question is:
How exactly are these copies of the functions with 'this' set to App actually being called? The app does not set them to a variable and then call that variable the way I had to when I was working in the console.
It just invokes the bound functions directly as soon as an event occurs on one of the jQuery elements. But I thought writing it this way would just return a copy of the function, and not run the function itself, if I am basing my assumptions on what I have figured out in the code I wrote above. I thought in order to invoke the function immediately, you would need to use call or apply.
I also realize that the app runs the bindEvents method when it starts (see line 46). So I understand that when you start the app, copies of the various functions are created with the correct 'this' bound to the functions. But...when/how do they actually get invoked without assigning them to variables? How are these copies accessed?
I think I have a flawed understanding of the bind method, so I would love some help. Thanks!
It sounds like you understand bind well enough. Perhaps there is some confusion with passing anonymous functions. As you know calling bind returns a new function and this can optionally be stored as a variable or passed as a function argument.
In the example below btn1 accepts a bound function as you've seen. This could also be written in a more long hand fashion with btn2. They're identical. btn3 doesn't receive a bound function, when its clicked its context is the button element, this looses all visibility of MagicalApp fucntions.
<button id="example1">button one bound</button>
<button id="example2">button one bound</button>
<button id="example3">button two unbound</button>
<script>
class MagicalApp {
add() {
console.log('this could do addition');
}
}
const app = new MagicalApp();
function contextOfEvent(event) {
console.log('contextSensitive', this.add)
}
const btn1 = document.querySelector("#example1");
btn1.addEventListener('click', contextOfEvent.bind(app));
const btn2 = document.querySelector("#example2");
const btn2ClickHandler = contextOfEvent.bind(app)
btn2.addEventListener('click', btn2ClickHandler);
const btn3 = document.querySelector("#example3");
btn3.addEventListener('click', contextOfEvent);
</script>

Are react's lifecycle method autobound? If not should we be binding them with .bind(this)?

I think the title is pretty self-descriptive.
I've building react components using the class notation, and I noticed that while handleSomething has to be manually bound to this, render and componentWillMount do not. Are method bound to this already? Is it ok to bind manually for notationally consistency's sake?
Understanding of 'this' in JavaScript
The 'this' keyword in a function is determined by the executing scope of the function. For example, the this in someFunction when calling with obj.someFunction() will be obj.
A more concrete example:
function handleClick() {
console.log(this.state.value);
}
var state = { value: 1 }; // declare a var in window
console.log("handleClick()");
handleClick(); // Logged 1. The 'this' in the method will be window, because the method is called in window
var obj = {
state: { value: 2 },
handleClick: function() {
console.log(this.state.value);
},
};
console.log("obj.handleClick();");
obj.handleClick(); // Logged 2. The 'this' is referred to obj because the method is called in obj.
// let's reassign the function to a temp var in window
var temp = obj.handleClick;
console.log("temp()");
temp(); // Logged 1. The 'this' in the function is referred to window because the method is called in window.
console.log("window.temp()");
window.temp(); // this is equal to the one above.
console.log("temp.bind(obj)");
temp.bind(obj)(); // Logged 2. Bind the method and call the method, so the 'this' in the function is referred to obj.
console.log("temp.bind(this)");
temp.bind(this)(); // Logged 1. Since this in the executing scope is window. This effectively is the same calling in this.
console.log("temp.bind(window)");
temp.bind(window)(); // Logged 1. This is equal to the one above.
Try it here: https://codepen.io/anon/pen/OvOpEa?editors=0012
A blog post about this: https://hackernoon.com/understanding-javascript-the-this-keyword-4de325d77f68
Back to your question
If you look at render, componentWillMount, and handleSomething you defined in your class, it will become apparent why you need to bind your handler to this.
render
// Rerender
ReactCurrentOwner.current = workInProgress;
var nextChildren = void 0;
{
ReactDebugCurrentFiber.setCurrentPhase('render');
nextChildren = instance.render();
if (debugRenderPhaseSideEffects) {
instance.render();
}
ReactDebugCurrentFiber.setCurrentPhase(null);
}
This is how react call redner(), where the instance is the object instance that has state, props, etc. You can try it very easily by putting a breakpoint in your render method and go back a call stack.
handleSomething
For example, if you define your class like this, with handleSomething as the onClick callback method of the button.
class Button extends Component {
handleSomething() {
// 'this' will be undefined.
}
render() {
return (<button onClick={this.handleSomething}>Test</button>);
}
}
If you click the button, this is how react calls the onClick handler method.
function callCallback() {
fakeNode.removeEventListener(evtType, callCallback, false);
// This is where react calls your method.
func.apply(context, funcArgs);
didError = false;
}
where func is handleSomething, and context is usually undefined in my debugging experience, and funcArgs is the arguments that being passed in the function.
apply is similar to bind. The first argument is used to specify the this of the function, and the second argument is an array of parameters to pass into the function.
See MDN for more information about apply: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
In this case, the method handleSomething is being called with undefined as this; Therefore, if you didn't bind the method, your this will be undefined.
I noticed that while handleSomething has to be manually bound to this,
render and componentWillMount do not. Are method bound to this
already?
They are called with the instance of your class, so they already have this as your instance without using bind. I guess you can kind of say it's already bound to this.
Is it ok to bind manually for notationally consistency's sake?
You don't need to bind this with react's lifecycle methods. If you really want to, I guess you can bind those methods to this as well (there might be some side effects that I don't know, since I didn't really look that deeply into their source), but this is like doing obj.handleClick.bind(obj)(); instead of obj.handleClick();. It is unnecessary and will spend some clock cycles doing something that's not needed.

CreateJS createjs.Ticker.off("tick", element.update) not working

I'm making a shooting game in which I need to update the state of bullets by binding them to the 'tick' event, however when calling the remove method to remove them from the 'tick' event it does not removes it. After creating a new instance this keeps getting updated instead of the one that was binded.
The methods 'add'/'remove' are used to bind/unbind the methods from the 'tick' event
class window.Stage
stage = undefined
counter = 0
fps = 60
add: (element) =>
element.id = counter++
stage.addChildAt(element.view, element.id)
element.listener = createjs.Ticker.on("tick", element.update)
remove: (element) =>
createjs.Ticker.off("tick", element.listener) # Not removing!
stage.removeChildAt(element.id)
update: () =>
stage.update()
This is how I'm calling the remove method in the Game class
run: () =>
if #gun? && !#gun.alive
#stage.remove(#gun)
#gun = undefined
if #player.shooting() && !#gun?
#gun = #player.shoot() # Ticker keeps updating new instance
#stage.add(#gun)
for bunker in #bunkers
if #gun? && bunker.gotShot(#gun)
#gun.alive = false
This is how bullets are created
class window.Player
shoot: =>
new Gun(#name, #x, #y - radius, false)
If there's any tutorial to better undestand how to correctly use listerners a link will be very much appreciated, thanks is advance.
The off() method requires you pass the method closure generated by calling on(), and not the original method that is passed. This is because the on() method generates a closure to maintain scope -- whereas addEventListener will not scope methods for you, requiring you to bind them yourself, or use global or anonymous handlers.
Make sure to store off the closure, and pass that instead. I am not familiar with the syntax in your example, so here is a vanilla JS example:
var listener = element.on("tick", handler, this);
element.off("tick", listener);
Note that the 3rd parameter is the scope the method should be called in, and if you don't pass it, it still generates a closure, and fires it in the element's scope instead of anonymously. There are also some other nice features of the on() approach, such as the "fire once" and event.remove() functionality.
You can always stick with the addEventListener/removeEventListener methods if you would prefer the same behaviour as DOM level 3 events.

JavaScript .click() unintentionally auto-starting

I created a custom variable/function that I am trying to execute when an element is clicked. For some reason, it decides to display onload and ignores the .click(). I've spent a while now trying to figure this out, but I'm not having much luck.
Here's my custom function:
var movebox = function (entry) {
$imagebox.css('left' , '0');
$('#wr').append(entry);
};
I'm attempting to call it like this, but it calls it when the page loads instead.
$l3.click(movebox('test'));
You're calling the movebox function immediately instead of passing the function as a reference to the click event handler. This is a common mistake in JavaScript. Instead, pass in your function inside of an anonymous function, like so:
$l3.click(function() {
movebox('test');
});
As an aside, the same mistake is oftentimes made with setTimeout, setInterval, addEventListener, and the infamous eval. Remember, when treating functions as arguments to another function, be sure to wrap them in anonymous functions.
You are calling the movebox then passing the returned value to click event handler, in this case you can use the .on() event registration helper to pass a data element to the event handler which can be accessed using the event object.
Try
var movebox = function (e) {
$imagebox.css('left' , '0');
$('#wr').append(e.data.entry);
};
$l3.on('click',{ entry: 'test'}, movebox);

Can a click handler be an object?

I'm trying to register on +1 clicks from within my module, which is wrapped as an annonymous function.
For this end, I created a global object MyModule, and exported my click handler function through it. The problem is - my click handler doesn't get called.
Live demo. Code:
// Initialize +1 button
gapi.plusone.go();
(function(){
window.MyModule = {};
function plusOneClicked() {
alert("+1!");
}
window.MyModule.plusOneClicked = plusOneClicked;
})()
...
<g:plusone callback='window.MyModule.plusOneClicked'></g:plusone>
When I give as a callback an external function, whose only purpose is to forward the calls to window.MyModule.plusOneClicked, it works:
function foo() {
window.MyModule.plusOneClicked();
}
...
<g:plusone callback='foo'></g:plusone>
Why would the click handler miss window.MyModule.plusOneClicked(), but find foo()?
Google is probably writing
window[callback]();
in their code.
This requires that the callback string refer to a property of window, not a property of another object.
I believe because callback expects a direct handler method (as in foo()) rather than a reference (as in window.MyModule.plusOneClicked). So basically, you cannot simply assign such a reference to click handler, but write a (wrapper) method as the handler and have it perform the necessary invocation.

Categories

Resources