I'm writing a Backbone program in Typescript, in which I am not able to initialize any events. Here's a test class that I've created to fix the problem. The function start() is not being called when the div is clicked on.
class TestView extends Backbone.View{
events = {
"click #testDiv" : "start"
}
start(){
console.log("Clicked");
}
constructor(options?){
super(options);
}
render(){
$root.html(getNewDiv("testDiv"));
$("#testDiv").css("height", 100).css("width", 100).css("background-color", "green");
console.log("Rendered");
return this;
}
}
function getNewDiv(id:string) {
return "<div id = \"" + id + "\"></div>"
}
new TestView().render();
Here's the console output:
Rendered
Here's the backbone typescript definition that I'm using:
https://github.com/borisyankov/DefinitelyTyped/blob/master/backbone/backbone.d.ts
Here's the CDN location for backboneJS
Minified : http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js
Non-Minified : http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone.js
I'm concerned if my syntax is right or this has something to do with the Typescript definition of Backbone.
UPDATE
The answer shown by Kevin Peel below is throwing an error, because in the Typescript definition file, "events" is defined as a function ( eg. events()). If I just create an "events" function, I get the error - "Uncaught TypeError: Object [object Object] has no method 'off'". If I use the getter method (eg. get events(){}), then nothing really happens on actually firing the event.
Using delegate Events
I tried to use the delegateEvents() function in Backbone.View which creates an error:
constructor(options:any, question:Question, div:JQuery) {
this.delegateEvents({
"click" : "clicked"
});
}
Error:
Uncaught TypeError: Object [object Object] has no method 'off' backbone.js:1082
h.extend.undelegateEvents backbone.js:1082
h.extend.delegateEvents backbone.js:1059
The problem is with where TypeScript defines the events. When you define events like this:
class TestView extends Backbone.View {
events = {
"click #testDiv": "start"
}
// ...
}
What TypeScript does is attaches events as a property after initializing the instance. So when Backbone is initializing the view, events haven't yet been attached, so Backbone isn't able to bind them.
Unfortunately, there doesn't seem to be a nice way to get TypeScript to assign events to the prototype, so you need to hack around it in some way.
The absolute easiest way is by setting the events on the prototype yourself:
class TestView extends Backbone.View {
// Get rid of the events declaration in here
// ...
}
TestView.prototype.events = {
"click #testDiv": "start"
}
A second possible way to get around the issue is using the getter functionality of TypeScript as suggested in this StackOverflow answer.
Finally, a third way would be to bind your events each time you render. This is another easy way to get around the TypeScript problem, but it might not be the best solution if the view gets rendered many times.
class TestView extends Backbone.View{
events = {
"click #testDiv" : "start"
}
// ...
render(){
$root.html(getNewDiv("testDiv"));
// Here is where you'll bind the events using Backbone's delegateEvents
this.delegateEvents();
$("#testDiv").css("height", 100).css("width", 100).css("background-color", "green");
console.log("Rendered");
return this;
}
}
// ...
new TestView().render();
I know is an old question but there is now another possibility, using es7 decorators, similar to what angular2 does:
#component({
events: {
"click #testDiv" : "start"
}
})
class TestView extends Backbone.View{
start(){
console.log("Clicked");
}
render(){
// ...
}
}
Decorator file
export function component(definition: any) {
return function (constructor: any) {
Object.assign(constructor.prototype, definition)
}
}
This way, all properties defined in the component decorator will be attached to the class prototype and will be available at instantiation time.
Another option besides using a class/constructor decorator could be to use a method decorator on the preinitialize method if you wish to have all the code inside the class braces like this:
class TestView extends Backbone.View {
#component({
events: {
"click #testDiv" : "start"
}
})
preinitialize(){}
start(){
console.log("Clicked");
}
render(){
// ...
}
}
Decorator file
export function component(definition: any) {
return function (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<Function>) {
Object.assign(target, definition)
}
}
Related
EDITED: Static methods don't have access to "this". The underlying question is then, how in reactjs should you organize the code, if you'd like to separate functionalities in different classes? the only way to call the methods of these classes is then by making them "static". Is it really the only way? What are you supposed to do? Create one big class so that all methods will have access to "this"?
EDITED2: What I have done is then to avoid writing a static method that needs to have access to the state. In particular, I've used a promise to return the value to the class that does have access to the state.
static Parse(cvs_string) {
return new Promise((resolve, reject) => {
Papa.parse(cvs_string, {
header: true,
skipEmptyLines: true,
complete: (results) => resolve(results)
});
});
}
EDITED3: But, as said in the comments, it's nonesense to build a class that also extends from Component if the main reason is to provide helper functions, so at the end:
import Papa from 'papaparse';
export const ParseCsv = (csv_string) => {
return new Promise((resolve, reject) => {
Papa.parse(csv_string, {
header: true,
skipEmptyLines: true,
complete: (results) => resolve(results)
});
});
}
---- [previous]
Why is this not working? Shouldn't I have access to setstate here?
import React, { Component } from 'react';
import Papa from 'papaparse';
class PapaParse extends Component {
constructor(props) {
super(props);
this.Parse = this.Parse.bind(this);
this.updateData = this.updateData.bind(this);
}
static Parse(cvs_string) {
Papa.parse(cvs_string, {
header: true,
skipEmptyLines: true,
// complete: (results) => { // this gives the same error
complete: function(results) {
PapaParse.updateData(results);
}
});
}
static updateData(results) {
console.log(results); // results are the expected ones
PapaParse.setState({data: results.data}); // here the error, or
this.setState({data: results.data}); // here the same error
}
}
export default PapaParse;
I can solve this by sending "this" as a variable, in
PapaParse.Parse(response, this);
and then in the PapaParse component
static Parse(cvs_string, that) {
...
PapaParse.updateData(results, that);
...
static updateData(results, that) {
...
that.setState({data: results.data});
So I understand that the "this" is lost when I'm calling a method of a componenet without invoking it with the "tag", and merely calling it as a static method.
Then, what I'm doing here is what I'm supposed to do? Or what would be the best way to do this?
Static methods are intended for code that doesn't depend on class instance, because there's none. There are not so many good use cases for static methods, because if a function isn't directly linked to a class as an entity, it possibly doesn't need to be a part of it. One of use cases is React component getDerivedStateFromProps hook which is pure function that needs to defined as a method (because it's a hook that should be accessed as class property by the framework), it forces a developer to not use class instance and focus on function input and output.
Since the method needs class instance and setState instance method in particular, static methods are not applicable here. None of these methods should be static:
class PapaParse extends Component {
Parse(cvs_string) {
Papa.parse(cvs_string, {
header: true,
skipEmptyLines: true,
complete: (results) => {
this.updateData(results);
}
});
}
updateData(results) {
console.log(results);
this.setState({data: results.data});
}
}
This is same problem as explained in this answer:
this.Parse = this.Parse.bind(this);
It's a mistake to bind static method to class instance, especially in a class that isn't a singleton by design and is expected to be instantiated multiple times (React component class is). There can be multiple class instances, this may result in bugs and memory leaks.
If Parse method is supposed to be triggered outside this class, this should be done in a way that is idiomatic to React, e.g. get PapaParse component ref in parent component and access instance method on it:
// in constructor
this.papaParseRef = React.createRef();
...
// in render
<PapaParse ref={this.papaParseRef}/>
The method will be available as this.papaParseRef.current.Parse() after render.
PapaParse.setState({data: results.data}); // here the error
Should be this.setState so you reference the instance, not the class.
Declare updatedata() as an arrow function.
updatedata=(results) => {
//this.setState will work here
}
When creating React components I sometimes on the web that methods are created with arrow function syntax, and sometimes without it. E.g.
class Component extends .... {
someFnk = (param) => { ... }
}
vs
class Component extends .... { someFnk(param) { ... } }
Which approach is better practice? Arrow function makes is safe to use this in function body, however I wonder when in React this could be an issue (when this could change)?
To rephrase the question: when arrow function syntax can safe me from creating a bug?
As long as you bind class methods in the constructor, the final, overall output is the same.
The following, once compiled operate in an identical manner.
class Foo extends React.Component {
constructor(props) {
super(props)
this.handleBla = this.handleBla.bind(this)
}
handleBla() {
}
}
class Foo extends React.Component {
handleBla = () => {
}
}
You say "why bind this when in React this won't change". That isn't actually true - all event handlers change the context of this. So make sure to either use the arrow function or bind for event handlers.
Transpiled
Once you transpile both through babel, you can see there is very little difference. The arrow function is simply mapped onto _this (remember this technique from pre-ES6 days?)
var Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
this.handleBla = this.handleBla.bind(this);
}
_createClass(Foo, [{
key: "handleBla",
value: function handleBla() {
console.log(this);
}
}]);
return Foo;
}();
var Foo = function Foo() {
var _this = this;
_classCallCheck(this, Foo2);
this.handleBla = function () {
console.log(_this);
};
};
Summary:
It's basically the same, but you must use bind context (either via the arrow function or bind) if you intend to use them with events and reference the component. This extremely common as most event handlers refer to state, setState or props, and so you will need the correct this
I have a quite big React.Component with more than ten bind(this) calls in its constructor.
I don't think .bind(this) in a constructor gives any help to understand my code especially when reading the code inside of render().
So, I came up with the following idea, however, do not find how to achieve this.
render() {
if (this.methodToBind.getThis() === this) {
this.methodToBind = this.methodToBind.bind(this);
}
}
Is there any possible way I can get this of a method (getThis() from the example above)?
If yes for the above, is it a good practice to do this?
rather then doing this,
constructor () {
super();
this.myFunc = this.myFunc.bind(this);
}
myFunc () {
// code here
}
You can do something like this.
constructor () {
super();
// leave the binding, it's just not what the cool kids do these days
}
myFunc = () => {
// see what I did here with = () => {}
// this will bind your `this` with it's parent
// lexical context, which is your class itself, cool right. Do this!
}
For a reference have a look at the MDN documentation for Arrow Functions
Where to Bind This?
When you create a function in a Class Based Component in React.js you must bind this in order to call it from the render method. Otherwise the function will not be in scope.
There are a few ways to do this.
Never bind this in the render method. Binding this in the render method will cause React to create a new function every time your Component is rerendered. This is why we most commonly bind in the constructor.
Bind in the constructor. By binding in the constructor, you can call your function in the render method by using this.FunctionName();
Example Bind This
Class MyClass extends Component {
constructor(props) {
super(props);
this.FunctionName = this.FunctionName.bind(this);
}
myFunc () {
// code here
}
render() {
this.FunctionName();
return(
<div>
...
</div>
);
}
}
User fat arrow functions instead of traditional function definitions. Fat arrow functions lexically bind the this value. So you do not have to add bind to the function in the constructor when you use the fat arrow function in the class.
Important - Fat arrow functions are not always available to use in a React class. Depending on how you setup React. You might have to install,
babel-plugin-transform-class-properties
Then add it to your .bablerc file like this,
{
"plugins": [
"transform-class-properties"
],
"presets": [
"react"
]
}
Example Fat Arrow
Class MyClass extends Component {
myFunc = () => {
// code here
}
render() {
this.FunctionName();
return(
<div>
...
</div>
);
}
}
Summary
Never bind a function in the render.
Always bind in the constructor when using a traditional function
this is automatically available on the function when using a fat arrow function.
Not sure.
I am usually doing something like this:
onClick={this.handleClick.bind(this)}
or:
onClick={e => this.handleClick(e)}
I heard that this is the cool way to define function instead of binding them in constructor:
class Comp extends React.Component {
delete = id => {
}
render() {}
}
but when I try to build I get:
Module build failed: SyntaxError: Unexpected token (7:15)
pointing to equal sign after delete
what I am missing?
To define class properties the way you are doing it, you need to activate the experimental babel feature transform-class-properties. This allows you to assign expressions like arrrow functions to class properties:
class Bork {
//Property initializer syntax
instanceProperty = "bork";
boundFunction = () => {
return this.instanceProperty;
}
//Static class properties
static staticProperty = "babelIsCool";
static staticFunction = function() {
return Bork.staticProperty;
}
}
Note that this feature is not yet part of the ECMAScript specification and may change in the future.
You need an additional babel plugin for this feature:
https://www.npmjs.com/package/babel-plugin-transform-class-properties
I am coding a simple app on reactjs-flux and everything works fine except I am receiving a warning from reactjs telling me that I am calling setState on unmounted components.
I have figured out this is because changelisteners to which components are hooked are not being removed from the store on componentWillUnmount. I know it because when I print the list of listeners from Eventemitter I see the listener which was supposed to be destroyed still there, and the list grows larger as I mount/unmount the same component several times.
I paste code from my BaseStore:
import Constants from '../core/Constants';
import {EventEmitter} from 'events';
class BaseStore extends EventEmitter {
// Allow Controller-View to register itself with store
addChangeListener(callback) {
this.on(Constants.CHANGE_EVENT, callback);
}
removeChangeListener(callback) {
this.removeListener(Constants.CHANGE_EVENT, callback);
}
// triggers change listener above, firing controller-view callback
emitChange() {
this.emit(Constants.CHANGE_EVENT);
}
}
export default BaseStore;
I paste the relevant code from a component experiencing this bug (it happens with all components, though):
#AuthenticatedComponent
class ProductsPage extends React.Component {
static propTypes = {
accessToken: PropTypes.string
};
constructor() {
super();
this._productBatch;
this._productBatchesNum;
this._activeProductBatch;
this._productBlacklist;
this._searchById;
this._searchingById;
this.state = this._getStateFromStore();
}
componentDidMount() {
ProductsStore.addChangeListener(this._onChange.bind(this));
}
componentWillUnmount() {
ProductsStore.removeChangeListener(this._onChange.bind(this));
}
_onChange() {
this.setState(this._getStateFromStore());
}
}
This is driving me pretty nuts at this point. Any ideas?
Thank you!
Short version: expect(f.bind(this)).not.toBe(f.bind(this));
Longer explanation:
The cause of the issue is that EventEmitter.removeListener requires that you pass a function you have previously registered with EventEmitter.addListener. If you pass a reference to any other function, it is a silent no-op.
In your code, you are passing this._onChange.bind(this) to addListener. bind returns a new function that is bound to this. You are then discarding the reference to that bound function. Then you try to remove another new function created by a bind call, and it's a no op, since that was never added.
React.createClass auto-binds methods. In ES6, you need to manually bind in your constructor:
#AuthenticatedComponent
class ProductsPage extends React.Component {
static propTypes = {
accessToken: PropTypes.string
};
constructor() {
super();
this._productBatch;
this._productBatchesNum;
this._activeProductBatch;
this._productBlacklist;
this._searchById;
this._searchingById;
this.state = this._getStateFromStore();
// Bind listeners (you can write an autoBind(this);
this._onChange = this._onChange.bind(this);
}
componentDidMount() {
// listener pre-bound into a fixed function reference. Add it
ProductsStore.addChangeListener(this._onChange);
}
componentWillUnmount() {
// Remove same function reference that was added
ProductsStore.removeChangeListener(this._onChange);
}
_onChange() {
this.setState(this._getStateFromStore());
}
There are various ways of simplifying binding - you could use an ES7 #autobind method decorator (e.g. autobind-decorator on npm), or write an autoBind function that you call in the constructor with autoBind(this);.
In ES7, you will (hopefully) be able to use class properties for a more convenient syntax. You can enable this in Babel if you like as part of the stage-1 proposal http://babeljs.io/docs/plugins/transform-class-properties/ . Then, you just declare your event listener methods as class properties rather than methods:
_onChange = () => {
this.setState(this._getStateFromStore());
}
Because the initializer for _onChange is invoked in the context of the constructor, the arrow function auto-binds this to the class instance so you can just pass this._onChange as an event handler without needing to manually bind it.
So I have found the solution, it turns out I only had to assign this._onChange.bind(this) to an internal property before passing it as an argument to removechangelistener and addchangelistener. Here is the solution:
componentDidMount() {
this.changeListener = this._onChange.bind(this);
ProductsStore.addChangeListener(this.changeListener);
this._showProducts();
}
componentWillUnmount() {
ProductsStore.removeChangeListener(this.changeListener);
}
I do not know, however, why this solves the issue. Any ideas?
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the exports component.
I am using the exact same implementation across multiple react components. i.e. this is repeated across several .jsx components.
componentDidMount: function() {
console.log('DidMount- Component 1');
ViewStateStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
console.log('DidUnMount- Component 1');
ViewStateStore.removeChangeListener(this._onChange);
},
_onChange:function()
{
console.log('SetState- Component 1');
this.setState(getStateFromStores());
},
Possible Solution
Currently the following is working out for me, but it has been a little temperamental. Wrap the call back in a function/named-function.
ViewStateStore.addChangeListener(function (){this._onChange});
one might also try
ViewStateStore.addChangeListener(function named(){this._onChange});
Theory
EventEmitter is for some reason getting confused identifying the callback to remove. Using a named function is perhaps helping with that.
Try removing the .bind(this) from your addChangeListener and removeChangeListener. They are already bound to your component when they get called.
I decided it so
class Tooltip extends React.Component {
constructor (props) {
super(props);
this.state = {
handleOutsideClick: this.handleOutsideClick.bind(this)
};
}
componentDidMount () {
window.addEventListener('click', this.state.handleOutsideClick);
}
componentWillUnmount () {
window.removeEventListener('click', this.state.handleOutsideClick);
}
}
This is a es6 problem. React.createClass binds 'this' properly for all function defined inside its scope.
For es6, you have to do something yourself to bind the right 'this'. Calling bind(this) however, creates a new function each time, and passing its return value to removeChangeListener won't match the function passed into addChangeListener created by an earlier bind(this) call.
I see one solution here where bind(this) is called once for each function and the return value is saved and re-used later. That'll work fine. A more popular and slightly cleaner solution is using es6's arrow function.
componentDidMount() {
ProductsStore.addChangeListener(() => { this._onChange() });
}
componentWillUnmount() {
ProductsStore.removeChangeListener(() => { this._onChange());
}
Arrow functions capture the 'this' of the enclosing context without creating new functions each time. It's sort of designed for stuff like this.
As you already got to know the solution here, I will try to explain what's happening.
As per ES5 standard, we used to write following code to add and remove listener.
componentWillMount: function() {
BaseStore.addChangeListener("ON_API_SUCCESS", this._updateStore);
},
componentWillUnmount: function() {
BaseStore.removeChangeListener("ON_API_SUCCESS", this._updateStore);
}
In above code, memory reference for the callback function (ie: this._updateStore) is same. So, removeChangeListener will look for reference and will remove it.
Since, ES6 standard lacks autobinding this by default you have to bind this explicitly to the function.
Note: Bind method returns new reference for the callback.
Refer here for more info about bind
This is where problem occurs. When we do this._updateStore.bind(this), bind method returns new reference for that function. So, the reference that you have sent as an argument to addChangeListener is not same as the one in removeChangeListener method.
this._updateStore.bind(this) != this._updateStore.bind(this)
Solution:
There are two ways to solve this problem.
1. Store the event handler (ie: this._updateStore) in constructor as a member variable. (Your solution)
2. Create a custom changeListener function in store that will bind this for you. (Source: here)
Solution 1 explanation:
constructor (props) {
super(props);
/* Here we are binding "this" to _updateStore and storing
that inside _updateStoreHandler member */
this._updateStoreHandler = this._updateStore.bind(this);
/* Now we gonna user _updateStoreHandler's reference for
adding and removing change listener */
this.state = {
data: []
};
}
componentWillMount () {
/* Here we are using member "_updateStoreHandler" to add listener */
BaseStore.addChangeListener("ON_STORE_UPDATE", this._updateStoreHandler);
}
componentWillUnmount () {
/* Here we are using member "_updateStoreHandler" to remove listener */
BaseStore.removeChangeListener("ON_STORE_UPDATE", this._updateStoreHandler);
}
In above code, we are binding this to _updateStore function and assigning that to a member inside constructor. Later we are using that member to add and remove change listener.
Solution 2 explanation:
In this method, we modify BaseStore functionalities. Idea is to modify addChangeListener function in BaseStore to receive second argument this and inside that function we are binding this to the callback and storing that reference, so that while removing change listener we can remove with that reference.
You can find complete code gist here and source here.