I have a listener setup in my componentDidMount:
updateBasketTotal: function() {
BasketService.getBasketTotal(function(data){
this.setState({
selectedPeopleCount: data.TotalMembers
});
}.bind(this));
},
componentDidMount: function() {
this.updateBasketTotal();
this.subscribeToChannel(basketChannel,"selectAll",this.listenerSelectAll);
this.subscribeToChannel(basketChannel,"removePersonFromBasket",this.listenerRemovePersonFromBasket);
this.subscribeToChannel(basketChannel,"addPersonToBasket",this.listenerAddPersonToBasket);
this.subscribeToChannel(basketChannel,"addArrayToBasket",this.listenerAddArrayToBasket);
},
listenerAddArrayToBasket: function(data){
BasketService.addPerson(data.arrayToPush,function(){
this.updateBasketTotal();
});
},
listenerAddPersonToBasket: function(data){
BasketService.addPerson(data.personId,function(){
this.updateBasketTotal();
});
},
listenerRemovePersonFromBasket: function(data){
BasketService.removePerson(data.personId,function(){
this.updateBasketTotal();
});
},
listenerSelectAll: function(data){
BasketService.selectAll(data.selectAll, function () {
this.updateBasketTotal();
});
}
However, if I publish a message when I'm not on this page, I get an error:
this.updateBasketTotal is not a function
Can anyone please tell me how I can use this.updateBasketTotal?
I think its a problem with 'this' but not sure how to fix it. Thanks in advance
UPDATE:
Have tried adding bind() to the listener:
listenerAddPersonToBasket: function(data){
BasketService.addPerson(data.personId,function(){
this.updateBasketTotal();
}.bind());
},
But no joy, any ideas?
I assume your component is unsubscribing to those channels in componentWillUnmount to avoid resource leaks and duplicate subscriptions.
The asynchronous callbacks should call isMounted to ensure the component is still mounted before attempting anything else.
BasketService.selectAll(data.selectAll, function () {
if (this.isMounted()) {
this.updateBasketTotal();
}
}.bind(this));
I don't know if the isMounted check will solve your problem since that may also not be a function anymore. If it isn't, you might consider adding your own property to track whether the component is mounted or not and check that rather calling a function.
listenerAddArrayToBasket: function(data) {
var _this = this;
BasketService.addPerson(data.arrayToPush,function() {
_this.updateBasketTotal();
});
}
or
listenerAddArrayToBasket: function(data) {
BasketService.addPerson(data.arrayToPush,function() {
this.updateBasketTotal();
}.bind(this));
}
React does not bind context by default, you have to do it yourself. In your example this would refer to callback function, not to react object.
You can either assign react context to a variable and use it inside your callback, or bind context directly to callback.
Here is the great article explaining contexts in JavaScript
Also in ES6 it's possible to use double arrow declaration
class SomeComponent extends React.Component {
updateBasketTotal() {
....
}
lisneterAddArrayToBasket() {
BasketService.addPerson(data.arrayToPush, () => {
this.updateBasketTotal()
})
}
}
You can use babel to compile your ES6 code to old plain ES5 :)
Related
I am trying to write a game using lance-gg library.
I tried to implement a simple aframe component, that print entity's object3D position and rotation in world space.
The problem is that I cannot access this from within the component event listener.
I have tried to search around I've found this [thread] (Aframe unregister component), so I guess the problem is the initialization order. I have tried to include a component directly from the index but it does't worked either.
// aSeparateFile.js
AFRAME.registerComponent(
'custom-component',
{
schema: {
controllerID: {
type: 'string',
default: 'none'
}
},
init: () => {
console.log('componet has been created');
console.log(this);
},
tick: () => {
console.log(this.el.object3D.rotation);
console.log(this.el.object3D.position);
}
}
);
this component was created in a separate file called aSeparateFile.js, I include this file from my AFrameRenderer extension. Like this:
import {AFRAMERenderer} from 'lance-gg';
import './aSeparateFile.js';
I would like to know the best way to register a custom component with lance-gg.
Don't use arrow functions that will bind the methods to the wrong this. Use regular functions instead:
AFRAME.registerComponent(
'custom-component',
{
schema: {
controllerID: {
type: 'string',
default: 'none'
}
},
init: function () {
console.log('componet has been created');
console.log(this);
},
tick: function () {
console.log(this.el.object3D.rotation);
console.log(this.el.object3D.position);
}
});
I created a Ext.Mixin component and would like to call a function of it from another component. How do I have to do that? Must be very obvious, but I can't see right now.
EDIT:
Ext.define('ABC.mixin.MyMixin', {
extend: 'Ext.Mixin',
mixinConfig: {
after: {
},
before: {
initComponent: 'init'
}
},
init: function () {
let me = this;
myfunction();
},
myfunction: function () {
//do stuff
}
}
How do I call myfunction() ?
When you include a mixin to a component all of the functions the mixin provides are included to the component itself.
So when you have a reference to your created component you cann call the function on the component itself.
Ext.define('ABC.mixin.MyMixin', {
extend: 'Ext.Mixin',
myfunction: function () {
//do stuff
}
});
Ext.define('ABC.view.MyView', {
mixins: ['ABC.mixin.MyMixin'],
// ...other config stuff
});
let myView = Ext.create('ABC.view.MyView'); // concreate Object of the class ABC.view.MyView
myView.myfunction(); // we can call the function of the mixin on the Object directly.
For more information see the ExtJs documentation
The API Docs seem to provide the information you need. You just include your mixin in the component you need, like so:
Ext.define('ABC.view.MyComponent', {
mixins: ['ABC.mixin.MyMixin'],
initComponent() {
this.myfunction();
this.callParent();
}
});
And from the component's scope, call the mixin's functions you need
I am trying to pass a Vue function to the lodash throttle method. Shouldn't I just be able to do something like this?
When I am trying to do this I am getting the following error:
Error in callback for watcher "query": "TypeError: Expected a function"
Watcher
watch: {
query() {
throttle(this.autocomplete(), 400);
}
}
Methods
methods: {
autocomplete() {}
}
Even though I am passing a function reference I am still getting an error message. If I wrap it with a anonymous function it won't fire:
throttle(() => { this.autocomplete(); }, 400);
I just checked and the autocomplete function does actually seem to fire regardless of the error that it is not a function in my example at the top.
What is going wrong here?
Edit:
Jsfiddle: http://jsfiddle.net/yMv7y/2780/
You are passing the return value of this.autocomplete() (maybe undefined) and not the function reference. If you want to do the latter, you have to omit the brackets:
watch: {
query() {
throttle(this.autocomplete, 400);
}
}
Working approach:
var demo = new Vue({
el: '#demo',
data: {
query: ''
},
watch: {
query: function() {
this.autocomplete()
}
},
methods: {
autocomplete: _.throttle(function() {
console.log('test');
}, 50)
}
})
<script src="http://vuejs.org/js/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<div id="demo" v-cloak>
<input type="text" v-model="query">
</div>
As #Bill Criswell commented,
This is creating a throttled function every time query changes. You
want to constantly call the same throttled function like the answer
below.
My guess is that you need to define the throttled function with a non-invoked callback in a variable, and then invoke that as a function:
var throttled = throttle(this.autocomplete, 400)
watch: {
query() {
throttled();
}
}
Just spent quite awhile trying to figure that one out...
I'm using qwest to query my endpoint as shown below, the onGetResourceCompleted handler fires as expected but data is undefined. Why?
var Actions = Reflux.createActions({
'getResource': { asyncResult: true }
});
Actions.getResource.listenAndPromise(function (id) {
return qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true });
});
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('');
},
onGetResourceCompleted: function (data) {
console.log('OK', data); // Get's called but data is undefined. Why?
}
});
I can see the data loads correctly by looking at dev tools as well as calling qwest in isolation by simply doing:
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true }).then(function(data) {
console.log('OK', data);
});
Also doing the following works:
ServiceActions.getResource.listen(function (id) {
ServiceActions.getResource.promise(
qwest.get('http://localhost:8000/my-data/'+id, null, { withCredentials: true })
);
});
I've put some comments on the cause of this "confirmed bug" in the original issue you opened at github.com/spoike/refluxjs.
So, though you are using the reflux features the way they are intended, and they're definitely creating a race condition without even returning the race results, I think you're in luck. It turns out the two particular features you're using in this combination with this type of request is a bit redundant when you already have a promise available. I'd recommend you just drop the onGetRequestCompleted handler entirely, and handle completion using the standard promise ways of handling resolved promises, which honestly will give you more flexibility anyways.
For example:
var MyStore = Reflux.createStore({
listenables: Actions,
init: function () {
Actions.getResource('')
.then() <-- this eliminates the need for onGetResourceCompleted
.catch() <-- or this instead/in addition
.finally() <-- or this instead/in additon
},
// no more onGetResourceCompleted
});
normally i would use this to load dependency
main: function() {
require(['views/home'], function(HomeView) {
_pageView.render(HomeView);
});
}
but now am looking of simplifying it by doing this
main: function() {
require(['views/home'], this.homeView);
},
homeView: function(HomeView) {
this.page = _pageView.render(HomeView);
}
but the keyword this is unrecognizable. How to make it recognizable.
Calling require like this:
require(['views/home'], this.homeView.bind(this));
should prevent this from getting set to a different value when RequireJS calls the callback.