I am trying to call a function inside child component through this.refs but i keep getting error that this function doesn't exist.
Uncaught TypeError: this.refs.todayKpi.loadTodaysKpi is not a function
Parent component:
class KpisHeader extends React.Component {
constructor() {
super();
this.onUpdate = this.onUpdate.bind(this);
}
render(){
return <div>
<DateRange ref="dateRange" onUpdate={this.onUpdate}/>
<TodayKpi ref="todayKpi" {...this.state}/>
</div>;
}
onUpdate(val){
this.setState({
startDate: val.startDate,
endDate: val.endDate
}, function(){
this.refs.todayKpi.loadTodaysKpi();
});
}
}
I want to get some data from DateRange component through function onUpdate, and then I want to trigger a function inside TodayKpi which fetches data from the server. For now it is just console.log("AAA");.
Child component:
class TodayKpi extends React.Component {
constructor() {
super();
this.loadTodaysKpi = this.loadTodaysKpi.bind(this);
}
render(){
console.log(this.props.startDate + " "+ this.props.endDate);
return <div className="today-kpi">
</div>;
}
loadTodaysKpi(){
console.log("AAAA");
}
}
How should I implement this?
For reasons I don’t yet grasp, React discourages calling child methods from the parent. However, they relent and give us an ‘escape hatch’ which allows just that. You were correct in thinking that ‘Refs’ were a part of that escape hatch. If, like me, you have read dozens of articles searching for this information, you will be well prepared to understand their description of the escape hatch
In your case, you may want to try something like this in your KpisHeader class.
Change this line
<TodayKpi ref="todayKpi" {...this.state}/>
to use a ref callback function something like this:
<TodayKpi ref={(todayKpiComponent) => this.todayKpiComponent = todayKpiComponent} {...this.state}/>
or, pre-ES6, this:
<TodayKpi
ref=
{
function (todayKpiComponent)
{
this.todayKpiComponent = todayKpiComponent
}
}
{...this.state}
/>
Then you will be able to access your todayKpi component methods from your KpisHeader class like this:
this.todayKpiComponent.someMethod();
Oddly, for me, inside the ref callback function, ‘this’ was the window rather than the parent component. So, I had to add
var self = this;
above the render method and use ‘self’ inside the ref callback function.
In my case I had an unknown number of dynamically generated children components, so I put each one into an array. I cleared the array in componentWillUpdate. It all seems to be working but I have an uneasy feeling especially given React’s distaste for calling children’s methods.
If you want the function/method to be called inside the child, you should pass it to the child from the parent to start with. The other thing that you need to change is onUpdate to onChange, assuming that you want to track every change to that field. The other alternative is to check when it is onSubmit, but it sounds like you want to have it happen every time the field is updated.
Related
I have the following code:
//Main Entry Point.
start() {
this.init();
this.gameLoop();
}
//Init, runs only once.
init() {
let initalEntity1: Entity = new Entity(10, 10, Gender.female);
let initalEntity2: Entity = new Entity(60, 60, Gender.male);
console.log("There are " + this.Entities.length + " Items in the entities Array")
this.Entities.push(initalEntity1, initalEntity2);
console.log("There are " + this.Entities.length + " Items in the entities Array")
}
gameLoop() {
console.log("Performing a Game Loop");
requestAnimationFrame(this.gameLoop);
//MAIN LOOP THROUGH ENTITIES
for (let i in this.Entities) {
this.Render.drawEntitysToScreen(this.Entities[i].EntityPosition, this.Entities[i].gender);
}
}
It enters into start() fine, and also performs all of the init() functionality. it them proceeds onto gameloop() which it will run once, however the line requestAnimationFrame(this.gameLoop); which is ment to retrigger the function to be called as a Canvas frame is causing the following error:
TypeError: this is undefined
trying to requestAnimationFrame(gameLoop); but it causes the typescript compiler to get upset...
This is due to how this binding works in javascript. The way by which you are passing this.gameLoop to requestAnimationFrame is essentially passing an unbound gameLoop function, and so when it is called, it has lost reference to its this.
There are a number of possible solutions to this problem:
You can bind this.gameLoop to this inside of the class constructor, like so:
constructor() {
this.gameLoop = this.gameLoop.bind(this);
}
You can bind this.gameLoop to this as part of the gameLoop method definition. Rather than defining gameLoop like
gameLoop() {
If you instead use
gameLoop = () => {
it will be automatically bound to this. This is a property of using the fat arrow for function declarations: it automatically performs binding to the this that exists at the function declaration.
You can change how you pass gameLoop to requestAnimationFrame:
requestAnimationFrame(() => this.gameLoop());
This again takes advantage of the automatic this binding performed by the arrow function, but instead of doing it as part of the class method declaration you can simply do it lazily at the time you need it to be bound.
Note, however, that doing it this way does mean that a new function will be created each time gameLoop is called.
When you call this.gameLoop() within start, the value for this inside gameLoop's body will be the class gameLoop belongs to, because you call gameLoop as a property of this (the class).
When you pass a function reference its value for this might be anything when the function is called from somewhere else.
Solution 1 | Using Function.prototype.bind
bind the value for this as you give the function to requestAnimationFrame. By doing this you explicitly say:
Let the argument to bind be this inside any call to gameLoop, regardless of how it is called, or where it is called from.
requestAnimationFrame(this.gameLoop.bind(this));
Note that bind returns you a new function, so the original gameLoop function that is still a property of your class remains unchanged.
Solution 2 | Using an arrow function
Define an arrow function to eventually execute the call to gameLoop instead of requestAnimationFrame. The this value within arrow functions is static, and is inherited from the execution context enclosing the function declaration.
requestAnimationFrame(() => this.gameLoop());
I am building a Polymer web component that should respond to mouse/touch events. I would like to bind the on-tap="{{$.myElement.show}}" where show is a function in the element:
show: function(e) {
// Do something with the event here
}
Is there anyway to define a public API in Polymer. The route I am going at the moment is to have attributes="event" on the element then the parent element has on-tap="{{updateEvent}}":
updateEvent: function(e) {
this.$.myElement.event = e;
}
The element then just has:
eventChanged: function() {
show(this.event);
}
Which just seems boilerplaty (think that is a made up word). Can I add the show function to the element prototype somehow?
If show() is a method on your element, just use on-tap="{{show}}". show() is part of the element's public APi.
But it looks like you want is to call another element's method? This came up recently in a pull request: https://github.com/Polymer/polymer-dev/pull/30
What you have with setting this.$.myElement.event = e; and using eventChanged() in myElement works. You can also just call the element's method directly:
updateEvent: function(e) {
this.$.myElement.show();
}
Here are a few other ways: http://jsbin.com/guyiritu/1/edit
I want to find elements with class starting with foo and attach event listeners to them.
This apparently worked $$('li[class^=foo]') but only if foo was the first class of the element. So I went for $$('li').hasClass([class^=foo]);.
That worked but now I wanted to use ...class^=foo]).addEvent(... and that didn't work.
Object [object Array] has no method 'addEvent' - Fiddle
Tried also
$$('li').hasClass([class^=foo]).each(function (el) {
el.addEvent('mouseenter', function () {
//...
But no sucess - Fiddle
What am I missing?
Well. several things you could do.
var li_elms = $$('li[class*=foo]');
will work fine but it will also include blahfoo
you can filter:
var li_elms = $$('li').filter(function(el){
return el.get('class').indexOf('foo') !== -1;
});
you can also delegate the event to the parent ul and check if the matching el should be considered at the time of firing.
Behold this example:
addEventListener("myEventType", myFunction("argument"));
function myFunction(args:String):Function {
return function(evt:Event):void {
trace(evt.currentTarget, "has", args);
};
}
dispatchEvent(new Event("myEventType", true));
It works.
Can I do something similar, but passing "argument" through dispatchEvent()?
It'd be very handy in a situation where dispatchEvent() is in a wholly separated class from addEventListener() and myFunction().
I'll be needing this a lot, so I want to do it without creating a custom event class for every situation.
You can use native flash.events.DataEvent for passing String parameter or create custom DataEvent with data:* property in all situations where you need to pass parameters to event handler.
If you want to customize the behavior of event listener in the place of adding event listener you can create "listener" object for holding this custom parameters (but I think this technique is more complicated than custom events):
addEventListener("myEventType", new EventListener("param1").onEvent);, whereEventListener is the class like this:
public class EventListener
{
private var params:*;
public function EventListener(params:*)
{
this.params = params;
}
public function onEvent(event:Event):void
{
trace("onEvent, params = ", params);
}
}
You could take a look at Signals (https://github.com/robertpenner/as3-signals). They are an alternative to Events and you can send whatever extra params you want with a Signal.
I'm having a slight issue with ActionScript 3 and I have come here to ask for some help.
I have two classes. One called Sledge and one called Sock, there is also the document class called Main.
My issues are as follows:
Inside of Sledge, I call a function that is defined inside of the Main document class. How would I go about telling the class to go to the document class and run that function? Would this also be the same for other classes or just for the document class?
Inside Sledge, I have the following statement: if(hitTestObject(sock.myHitArea)) { /* somecode*/ }
sock is an instance of another seperate class, and by this point has already been created. However when I try and run this I am told it is not defined. How would i go about solving this?
There's some ambiguity issues with how you expressed your question. It would help if you posted a short form of the code for the problem.
However, I'll try to answer the first question:
Inside of Sledge, I call a function that is defined inside of the Main document class. How would I go about telling the class to go to the document class and run that function?
You would want to pass the Main class to the Sledge class or use events which is preferable. If pass the class it will look like this...
class Sledge {
private var main:Main;
function Sledge(main:Main) {
this.main = main;
}
function doSomething():void {
main.runSomeFunction();
}
}
Or if using events:
class Main {
private var sledge:Sledge;
function Main() {
sledge = new Sledge();
sledge.addEventListener("mainDoSomething", doSomething);
}
private function doSomething(e:Event):void {
// .... do stuff
}
}
class Sledge extends EventDispacter {
function Sledge() {
}
public function doSomething():void {
dispatchEvent(new Event("mainDoSomething"));
}
}