Refactoring react this binding - reactjs

I have this code
class BottomPanel extends React.Component<Props, {}> {
constructor(props) {
super(props);
this.dropDownUpdate = this.dropDownUpdate.bind(this);
}
dropDownUpdate = e => this.setState({ dropDownValue: e.currentTarget.textContent });
I was wondering if I can refactor
this.dropDownUpdate = this.dropDownUpdate.bind(this);
to
this.dropDownUpdate = this.dropDownUpdate;
if so, why or why not? I just saw it somewhere being done this way
this.dropDownUpdate = this.dropDownUpdate;
I'm not sure if I can apply it to my code.

One thing to be aware of, is that when you write:
class BottomPanel extends React.Component<Props, {}> {
dropDownUpdate = e => this.setState({ dropDownValue: e.currentTarget.textContent });
}
This (currently) is not valid ES6 syntax. You can't assign a class variable with an equals sign that way. The reason it works is because of the transform-class-properties babel plugin, more on that below.
See this question and answer for more details:
ES6 class variable alternatives
Technically, the right way to declare a class variable is with the this keyword like:
class BottomPanel extends React.Component<Props, {}> {
constructor(props) {
this.dropDownUpdate = this.dropDownUpdate.bind(this)
}
dropDownUpdate(e) {
this.setState({ dropDownValue: e.currentTarget.textContent });
}
}
For why you can't just do:
class BottomPanel extends React.Component<Props, {}> {
constructor(props) {
this.dropDownUpdate =e => this.setState({ dropDownValue: e.currentTarget.textContent });
}
}
see this post: Can I use arrow function in constructor of a react component?
Now the thing is - I assume that you are using Create React App - which comes with the transform-class-properties babel plugin - which is why that first declaration works.
Take a look at this question as to why you would want to to the this.dropDownUpdate = this.dropDownUpdate.bind(this); trick. It's not necessary - but you run into a debugger issue (the browser not being aware of the transform-class-properties babel transform).
Chrome, Firefox debuggers not displaying the correct value for 'this' in a react app
Otherwise you can just clear your class methods like:
class BottomPanel extends React.Component<Props, {}> {
dropDownUpdate = e => this.setState({ dropDownValue: e.currentTarget.textContent });
}

Since dropDownUpdate is an Arrow function, It should work as expected without
this.dropDownUpdate = this.dropDownUpdate; Or
this.dropDownUpdate = this.dropDownUpdate.bind(this);
In Arrow functions, value of this is lexically bound, unlike regular functions where the value of this changes based on the context in which the function is called.

Just define your function as below.
dropDownUpdate = (event) => {
....
}
It wont need to bind in constructor

Related

Where should a state be defined?

What is the difference between these two constructs of defining state in React?
class ProductsPage extends Component {
constructor(props) {
super(props);
this.state = {
products: []
};
}
...
}
and this:
class ProductsPage extends Component {
state = {
products: []
};
...
}
Both of them work well when coded in ES6. However, the lower one doesn't seem to work in typescript 3. I have the following:
interface IState {
products: IProduct[];
}
class ProductsPage extends Component<{}, IState> {
state = {
products: []
};
public componentDidMount() {
this.setState({ products });
}
public render() {
return (
<div className="page-container">
<ul className="product-list">
{this.state.products.map(p => (
<li className="product-list-item" key={p.id}>
{p.name}
</li>
))}
</ul>
</div>
);
}
}
and the ts compiler flagged an error saying: Property id does not exist on type 'never'
Why is that?
The second form is class properties which is a stage 3 JavaScript proposal (meaning it's not part of the language yet). Adding properties on the constructor is the old ES2015 way (called maximally minimal classes).
In your case there is no functional difference.
TypeScript requires you to declare class fields for type safety - hence the warning.
How to define the state for a React component is as subjective as the coding styles React promotes itself. Usually I go the very strict route which looks as follows:
type State = {
someStateVar: number[];
};
export class MyComponent extends Component<{}, State> {
public readonly state: State = {
someStateVar: [],
};
public async componentDidMount() {
// Dynamically fetch data (maybe via axios) and populate the new state
const data = await axios.get<number[]>(...);
this.setState({ someStateVar: data });
}
}
As you can see, I explicitly mark state as readonly just make sure, nobody attempts to write directly to it (even though IDEs and linters can check for those errors without the precaution nowadays).
Another reason why I prefer to not set the state manually with an assigment is that it might encourage wrong handling of state. You are never to assign something directly to state in a class method without the use of setState.
Furthermore I am basically defining the defaults right away and populating the state with dynamic data in componentDidMount. This approach allows you to keep the code concise by dropping an explicit constructor definition as you should move such an initialization to componentDidMount anyways and use the constructor only for binding methods if you don't use the class member arrow notation for those in the first place.

What's different between two ways of defining React Component?

There're 2 ways to define a React component.
First one is like below.
class SomeComponent extends React.Component {
constructor (props) {
super(props)
this.state = {
someState: false
}
this._handleOnChangeState = this._handleOnChangeState.bind(this)
}
_handleOnChangeState (e) {
this.setState({ someState: e.target.value })
}
....
}
Second one is like below.
class SomeComponent extends React.Component {
state = {
someState: false
}
_handleOnChangeState = (e) => {
this.setState({ someState: e.target.value })
}
....
}
These two codes are the same function, but I guess there's some different something like memory usage or etc.
Can someone make it clearly? Thanks in advance!
This is a new proposal (class fields) for ES which is in stage 3 right now. To run a code written in this way you need a transpiler like Babel and an appropriate plugin.
Before transpile:
class A {
static color = "red";
counter = 0;
handleClick = () => {
this.counter++;
}
}
After transpile (with stage 2 on Babel Repl):
class A {
constructor() {
this.counter = 0;
this.handleClick = () => {
this.counter++;
};
}
}
A.color = "red";
In addition to the official proposal 2ality blog post is a good source to see what are the details.
Here is a reddit post if you have time to read the discussion storm what is the reason behind this proposal :)
The arrow function here is a different story. You can use instance properties without constructor and mix your code with standard functions. But when you want to use something like that this won't work:
class App extends React.Component {
state = { bar: "baz"}
foo() { console.log(this.state.bar) };
render() {
return <div><button onClick={this.foo}>Click</button></div>;
}
}
We need to bind our function in somehow like:
return <div><button onClick={this.foo.bind(this)}>Click</button></div>
But, binding our function in a JSX prop is no so good since it will create our function in each render.
One way to do this nicely bind in our constructor:
constructor(props) {
super(props);
this.foo = this.foo.bind( this );
}
But, if I have to write a constructor what is the point? This is why you see arrow functions everywhere where we define the classes like your second example. No need to bind to function thanks to arrow functions. But it is not directly related to this new proposal I think.
The first one is the traditional approach and the second one is when you babel-transform-class-properties plugin.
In the second type babel does the same thing under the hood, therefore it is a matter of convenience.

Is the constructor still needed in React with autobinding and property initializers

I am refactoring an es6 class based React component that uses the normal constructor, and then binds methods, and defines state/attributes within that constructor. Something like this:
class MySpecialComponent extends React.Component {
constructor(props) {
super(props)
this.state = { thing: true }
this.myMethod = this.myMethod.bind(this)
this.myAttribute = { amazing: false }
}
myMethod(e) {
this.setState({ thing: e.target.value })
}
}
I want to refactor this so that I am autobinding the functions, and using property initializers for the state and attributes. Now my code looks something like this:
class MySpecialComponent extends React.Component {
state = { thing: true }
myAttribute = { amazing: false }
myMethod = (e) => {
this.setState({ thing: e.target.value })
}
}
My question is, do I still need the constructor? Or are the props also autobound? I would have expected to still need the constructor and included super(props), but my code seems to be working and I'm confused.
Thanks
From my understanding, you don't need to type out a constructor at all when using class properties (as in your second code example). The accepted answer states that you do need one if you "need to reference the props in your initial state object," but if you're using said class properties, then you're probably using Babel to transpile it, in which case a constructor is used, it's just being done behind the scenes. Because of this, you don't need to add a constructor yourself, even if you are using props in state.
See this aricle for better examples and a better explanation.
You don't need an explicitly defined constructor unless you need to reference the props in your initial state object.
You don't need to define a constructor explicitly , and then do super(props).You can access the props as in the example below. i.e. 'prop1'
class MySpecialComponent extends React.Component {
state = {
thing: true ,
prop1:this.props.prop1
}
myAttribute = { amazing: false }
myMethod = (e) => {
this.setState({ thing: e.target.value })
}
render(){
console.log(this.state.prop1);
return(
<div>Hi</div>
);
}
}
ReactDOM.render(<MySpecialComponent prop1={1}/> , mountNode);

How to properly use arrows for ES6 React class functions? [duplicate]

I'm new to using ES6 classes with React, previously I've been binding my methods to the current object (show in first example), but does ES6 allow me to permanently bind a class function to a class instance with arrows? (Useful when passing as a callback function.) I get errors when I try to use them as you can with CoffeeScript:
class SomeClass extends React.Component {
// Instead of this
constructor(){
this.handleInputChange = this.handleInputChange.bind(this)
}
// Can I somehow do this? Am i just getting the syntax wrong?
handleInputChange (val) => {
console.log('selectionMade: ', val);
}
So that if I were to pass SomeClass.handleInputChange to, for instance setTimeout, it would be scoped to the class instance, and not the window object.
Your syntax is slightly off, just missing an equals sign after the property name.
class SomeClass extends React.Component {
handleInputChange = (val) => {
console.log('selectionMade: ', val);
}
}
This is an experimental feature. You will need to enable experimental features in Babel to get this to compile. Here is a demo with experimental enabled.
To use experimental features in babel you can install the relevant plugin from here. For this specific feature, you need the transform-class-properties plugin:
{
"plugins": [
"transform-class-properties"
]
}
You can read more about the proposal for Class Fields and Static Properties here
No, if you want to create bound, instance-specific methods you will have to do that in the constructor. However, you can use arrow functions for that, instead of using .bind on a prototype method:
class SomeClass extends React.Component {
constructor() {
super();
this.handleInputChange = (val) => {
console.log('selectionMade: ', val, this);
};
…
}
}
There is an proposal which might allow you to omit the constructor() and directly put the assignment in the class scope with the same functionality, but I wouldn't recommend to use that as it's highly experimental.
Alternatively, you can always use .bind, which allows you to declare the method on the prototype and then bind it to the instance in the constructor. This approach has greater flexibility as it allows modifying the method from the outside of your class.
class SomeClass extends React.Component {
constructor() {
super();
this.handleInputChange = this.handleInputChange.bind(this);
…
}
handleInputChange(val) {
console.log('selectionMade: ', val, this);
}
}
You are using arrow function and also binding it in constructor. So you no need to do binding when you use arrow functions
class SomeClass extends React.Component {
handleInputChange = (val) => {
console.log('selectionMade: ', val);
}
}
OR you need to bind a function only in constructor when you use normal function like below
class SomeClass extends React.Component {
constructor(props){
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(val){
console.log('selectionMade: ', val);
}
}
Also binding a function directly in render is not recommended. It should always be in constructor
I know this question has been sufficiently answered, but I just have a small contribution to make (for those who don't want to use the experimental feature). Because of the problem of having to bind multiple function binds in the constructor and making it look messy, I came up with a utility method that once bound and called in the constructor, does all the necessary method bindings for you automatically.
Assume I have this class with the constructor:
//src/components/PetEditor.jsx
import React from 'react';
class PetEditor extends React.Component {
constructor(props){
super(props);
this.state = props.currentPet || {tags:[], photoUrls: []};
this.tagInput = null;
this.htmlNode = null;
this.removeTag = this.removeTag.bind(this);
this.handleChange = this.handleChange.bind(this);
this.modifyState = this.modifyState.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
this.addTag = this.addTag.bind(this);
this.removeTag = this.removeTag.bind(this);
this.savePet = this.savePet.bind(this);
this.addPhotoInput = this.addPhotoInput.bind(this);
this.handleSelect = this.handleSelect.bind(this);
}
// ... actual method declarations omitted
}
It looks messy, doesn't it?
Now I created this utility method
//src/utils/index.js
/**
* NB: to use this method, you need to bind it to the object instance calling it
*/
export function bindMethodsToSelf(objClass, otherMethodsToIgnore=[]){
const self = this;
Object.getOwnPropertyNames(objClass.prototype)
.forEach(method => {
//skip constructor, render and any overrides of lifecycle methods
if(method.startsWith('component')
|| method==='constructor'
|| method==='render') return;
//any other methods you don't want bound to self
if(otherMethodsToIgnore.indexOf(method)>-1) return;
//bind all other methods to class instance
self[method] = self[method].bind(self);
});
}
All I now need to do is import that utility, and add a call to my constructor, and I don't need to bind each new method in the constructor anymore.
New constructor now looks clean, like this:
//src/components/PetEditor.jsx
import React from 'react';
import { bindMethodsToSelf } from '../utils';
class PetEditor extends React.Component {
constructor(props){
super(props);
this.state = props.currentPet || {tags:[], photoUrls: []};
this.tagInput = null;
this.htmlNode = null;
bindMethodsToSelf.bind(this)(PetEditor);
}
// ...
}

React only binds Component methods to this - work around?

Is there a way to avoid the boilerplate when using ES6 with react 0.14?
Until now I didn't have to worry about my function to be bound to the Component I created but that is no longer (why?!?) the case and the Component is only bounded to the Component super class (If I understood the errors correctly).
So what I really need to do each time I create a new class is to add this code to the constructor:
class CustomComp extends React.Component {
constructor() {
super();
this.newFunction = this.newFunction.bind(this);
}
newFunction(){
console.log('This is user defined function');
}
render() {
return <button onClick={this.newFunction}>Click</button>
}
}
So if I wont bind newFunction it will fail (no props, state or anything).
Is there a way to work around this?
From the React documentation:
No Autobinding
Methods follow the same semantics as regular ES6 classes, meaning that
they don't automatically bind this to the instance. You'll have to
explicitly use .bind(this) or arrow functions =>.
So, no there is not an automatic way to bind all methods that is new in 0.14. But, as the documentation suggests, you can:
1) use arrow functions (However, if you are using Babel, you need stage 0):
class CustomComp extends React.Component {
constructor() {
super();
}
newFunction = () => {
console.log('This is user defined function');
}
render() {
return <button onClick={this.newFunction}>Click</button>
}
}
2) you can bind in place:
class CustomComp extends React.Component {
constructor() {
super();
}
newFunction() {
console.log('This is user defined function');
}
render() {
return <button onClick={this.newFunction.bind(this)}>Click</button>
}
}
3) you can use an arrow function in place (which is like a bind):
class CustomComp extends React.Component {
constructor() {
super();
}
newFunction() {
console.log('This is user defined function');
}
render() {
return <button onClick={() => this.newFunction()}>Click</button>
}
}
I tend to use number 2 & 3 if I only have a 1-2 methods. Number 1 is good, but you have to remember the syntax for every method definition. If I have a lot of methods, I do tend to bind in the constructor.

Resources