How to bind this to React ES6 getter and setter functions? - javascript

When we want to use this from inside an ES6 function, we need to say so in the constructor.
export class MyComponent extends React.Component {
constructor(){
super();
this.myFunc = this.myFunc.bind(this);
}
myFunc(){
/*
Now inside this function, this is now referring to our
component. We can now access props, states, and refs
because we declared 'this.myFunc = this.myFunc.bind(this);'
in the constructor.
*/
}
}
But there are getter functions, and I cannot use the same function binding "syntax" as I do with regular functions:
get value(){
return this.state.oneOfMyValues;
/*
The above does not work. this.state is undefined, so is .refs,
so is .props, because 'this' is not the component itself.
*/
}
As I've said, binding value() in the constructor doesn't work:
constructor(){
super();
this.myFunc = this.myFunc.bind(this); // This works. It's a regular function.
this.value = this.value.bind(this);
/* No compilation errors for this code, but it doesn't work. The 'this'
inside the get value() function is still not the component.
*/
this.state = {};
/* as suggested, but this.state is still undefined from
inside get value().
*/
}
I cannot use arrow functions on a getter function like I can with a regular function.
HOW do we bind a getter (and probably also a setter) function to the component so that the 'this' inside it refers to the component? As much as possible, I don't want to use React.createClass({}) "syntax". If I have to go back to the createClass way, what is the point of being able to write getter and setter functions in ES6 if we don't have access to our component via 'this'?
Usage This is parentcomponent.js
#import React from 'react';
#import { MyComponent } from './mycomponent.js';
export class ParentComponent extends React.Component {
clickMe = () => {
console.log(this.refs.myComponent.value);
/*
Does not return anything because the get value()
function of MyComponent has no access to its state,
props, etc.
*/
}
render(){
return(
<div className="parent-component">
<span onClick={this.clickMe}>Click Me</span>
<MyComponent ref="myComponent" />
</div>
);
}
}

Following your comment, it is an issue of this.state initialisation . Indeed , in the getter, you are using this.state. oneOfMyValues however this.state is not defined .
Then , the solution is to declare this.state = {} inside the constructor of component.
constructor(){
super();
this.myFunc = this.myFunc.bind(this); // This works. It's a regular function.
// this.value = this.value.bind(this); <-- NO NEED
this.state = {} ; //⚠️
}
For the binding, you can log the this.constructor inside getter and you will be sure that getters and setters does not require manual binding.
get value(){
console.log(this.constructor.name) //🔮 Expect to log "MyComponent"
return this.state.oneOfMyValues;
}

Looks like your ParentComponent's 'clickMe' handler is not bound to its instance 'this'.
You can either do it here:
<span onClick={this.clickMe}>Click Me</span>
to
<span onClick={this.clickMe.bind(this)}>Click Me</span>
OR you can do it in the ParentComponent's constructor:
export class ParentComponent extends React.Component {
constructor(props){
super(props);
this.clickMe = this.clickMe.bind(this);
}
...
}
#Felix Kling's JSfiddle illuminated this when he said his code worked. He binds his clickMe, therefore it worked. jsfiddle.net/hxL51764

Related

React - Calling setState in a function that's called from another class throws not a function exception

What I'm trying to achieve here is, I want the value to be updated in the imported class B's state by calling the B's function init() from the class A. I'm initializing a new Object of B class and calling the init function through the object.
Class A
import B from 'b.js'
class A extends Component{
componentDidMount(){
const b=new B();
b.init("hey");
}
}
In Class B: I'm updating the state using the init function, but it appears that I'm getting setState is not a function error. I have also tried binding init function in the constructor, but the error stays the same.
class B extends Component{
constructor(props){
super(props);
state = {
text:""
}
}
init=(text)=>{
this.setState({text})
}
}
You don't really need init, that is what the constructor is for. You should pass in the text as a prop to component B when you create it:
import B from 'b.js';
class A extends Component {
componentDidMount() {
const b = new B({ initialText: 'hey' });
}
}
Then you can set the state inside the constructor for B.
state = {
text: prop.initialText,
};
Be careful though, setting the state from a prop is generally a bad idea. See the note block here https://reactjs.org/docs/react-component.html#constructor for more details.

Why is "this" bound different in some instances, but the same in others in React?

In this component, I'm not able to call a function in the render method by going this.functionName if it's not an arrow function. Howevever, I am able to call this.setState effectively in both an arrow function and a regular function. Why is "this" different in some situations, but seemingly the same in other situations in a React component like this?
import React from 'react';
class Address extends React.Component {
state = {
fullAddress: "5001"
}
componentDidMount() {
this.setState({
fullAddress: "hello"
})
}
hello = () => {
this.setState({
fullAddress: "hello1"
})
}
logMessage() {
console.log(this.state.fullAddress);
}
render() {
return (
<div className="address">
{this.state.fullAddress}
<input type="button" value="Log" onClick={this.hello} />
</div>
);
}
}
export default Address;
In your example, logMessage will probably break since you need to specify your this context to it.
In this case, simply bind it in Address's constructor like so:
class Address extends Component {
constructor(props) {
super(props)
this.logMessage = this.logMessage.bind(this)
}
}
A second approach would be the same you already used with hello as arrow function like. Arrow functions keep your current context (this) and that's why you have access to this.setState inside hello's body for example.

How to avoid using inline functions in React (ES6) for functions that take arguments

I'm trying to follow the suggestion in this react-eslint doc to avoid using inline functions.
I have a div with an onClick funciton like so:
onClick={ () => this.props.handleClick(some_constant) }
This works perfectly fine, however I don't want to have an inline function. When I try to abstract it by following the pattern in the provided link above, I get a setState error that runs infinitely.
class Foo extends React.Component {
constructor() {
super();
this._handleClickWrapper = this.handleClickWrapper.bind(this);
}
_handleClickWrapper() {
// handleClick is a callback passed down from the parent component
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this._handleClickWrapper}>
Hello!
</div>
);
}
}
What needs to be done so that I can avoid using inline functions?
Edit:
I made a serious typo, but in my code, I have what is currently reflected and it is still causing the error.
You bound the wrong function to this. It should be:
this._handleClickWrapper = this._handleClickWrapper.bind(this);
This way _handleClickWrapper will always be bound to the context of the component.
If you really really really want to follow the jsx-no-bind rule, you can create a new component and pass someConstant in as a prop. Then the component can call your callback with the value of someConstant:
class FooDiv extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
render() {
return <div onClick={this.handleClick}>Hello!</div>
}
handleClick() {
this.props.onClick(this.props.someConstant);
}
}
Then your Foo component can just do this:
class Foo extends React.Component {
render() {
const someConstant = ...;
return (
<FooDiv
onClick={this.props.handleClick}
someConstant={someConstant}
/>
);
}
}
Having said that, I would recommend not following jsx-no-bind and just use bind or arrow functions in render. If you're worried about performance due to re-renderings caused by using inline functions, check out the reflective-bind library.
There is a typo
this._handleClickWrapper = this.handleClickWrapper.bind(this);
should be
this._handleClickWrapper = this._handleClickWrapper.bind(this);
in your constructor you forgot to pass props to super()
constructor(props) {
super(props);
this._handleClickWrapper = this._handleClickWrapper.bind(this);
}
Tipp: You can avoid binding (and even the constructor) by using arrow functions declaration inside the class (babel-preset-es2016).
class Foo extends React.Component {
state = {} // if you need it..
onClick = () => {
this.props.handleClick(some_constant)
}
render() {
return (
<div onClick={this.onClick}>
Hello!
</div>
);
}
}
This way you components gets smaller, and easier to read.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding

Why is this undefined in function when calling .bind(this)?

In my component, I'm calling a function defined in another file like this:
import React from 'react';
import { widgetComponentDidMount } from "../../SharedData/widget";
export default class Component extends React.Component {
constructor(props) {
super(props);
};
componentDidMount(){
widgetComponentDidMount(this).bind(this);
}
//render, etc
};
But when I put a debugger into the function's definition, "this" is undefined:
export function widgetComponentDidMount() {
var _this = this;
debugger
//_this is undefined
}
I know I can pass this as an argument and reference it that way, but I'd rather avoid having to pass it every time. Isn't that the purpose of bind? I've only ever been able to use it within the same file. Any ideas why it's undefined and how I can access "this" without passing it as an argument?
You're executing the function before you call bind. You'd have to bind before executing:
widgetComponentDidMount.bind(this)();
If it were me, I'd actually perform the bind in the constructor method and store the result so you don't need to create a new function for each call:
export default class Component extends React.Component {
constructor(props) {
super(props);
this.widgetComponentDidMount = widgetComponentDidMount.bind(this);
}
componentDidMount() {
this.widgetComponentDidMount();
}
}

React constructor [duplicate]

When is it important to pass props to super(), and why?
class MyComponent extends React.Component {
constructor(props) {
super(); // or super(props) ?
}
}
There is only one reason when one needs to pass props to super():
When you want to access this.props in constructor.
Passing:
class MyComponent extends React.Component {
constructor(props) {
super(props)
console.log(this.props)
// -> { icon: 'home', … }
}
}
Not passing:
class MyComponent extends React.Component {
constructor(props) {
super()
console.log(this.props)
// -> undefined
// Props parameter is still available
console.log(props)
// -> { icon: 'home', … }
}
render() {
// No difference outside constructor
console.log(this.props)
// -> { icon: 'home', … }
}
}
Note that passing or not passing props to super has no effect on later uses of this.props outside constructor. That is render, shouldComponentUpdate, or event handlers always have access to it.
This is explicitly said in one Sophie Alpert's answer to a similar question.
The documentation—State and Lifecycle, Adding Local State to a Class, point 2—recommends:
Class components should always call the base constructor with props.
However, no reason is provided. We can speculate it is either because of subclassing or for future compatibility.
(Thanks #MattBrowne for the link)
In this example, you are extending the React.Component class, and per the ES2015 spec, a child class constructor cannot make use of this until super() has been called; also, ES2015 class constructors have to call super() if they are subclasses.
class MyComponent extends React.Component {
constructor() {
console.log(this); // Reference Error
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
By contrast:
class MyComponent extends React.Component {
constructor() {
super();
console.log(this); // this logged to console
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
More detail as per this excellent stack overflow answer
You may see examples of components created by extending the React.Component class that do not call super() but you'll notice these don't have a constructor, hence why it is not necessary.
class MyOtherComponent extends React.Component {
render() {
return <div>Hi {this.props.name}</div>;
}
}
One point of confusion I've seen from some developers I've spoken to is that the components that have no constructor and therefore do not call super() anywhere, still have this.props available in the render() method. Remember that this rule and this need to create a this binding for the constructor only applies to the constructor.
When you pass props to super, the props get assigned to this. Take a look at the following scenario:
constructor(props) {
super();
console.log(this.props) //undefined
}
How ever when you do :
constructor(props) {
super(props);
console.log(this.props) //props will get logged.
}
When implementing the constructor() function inside a React component, super() is a requirement. Keep in mind that your MyComponent component is extending or borrowing functionality from the React.Component base class.
This base class has a constructor() function of its own that has some code inside of it, to setup our React component for us.
When we define a constructor() function inside our MyComponent class, we are essentially, overriding or replacing the constructor() function that is inside the React.Component class, but we still need to ensure that all the setup code inside of this constructor() function still gets called.
So to ensure that the React.Component’s constructor() function gets called, we call super(props). super(props) is a reference to the parents constructor() function, that’s all it is.
We have to add super(props) every single time we define a constructor() function inside a class-based component.
If we don’t we will see an error saying that we have to call super(props).
The entire reason for defining this constructor() funciton is to initialize our state object.
So in order to initialize our state object, underneath the super call I am going to write:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
// React says we have to define render()
render() {
return <div>Hello world</div>;
}
};
So we have defined our constructor() method, initialized our state object by creating a JavaScript object, assigning a property or key/value pair to it, assigning the result of that to this.state. Now of course this is just an example here so I have not really assigned a key/value pair to the state object, its just an empty object.
Dan Abramov wrote an article on this topic:
Why Do We Write super(props)?
And the gist of it is that it's helpful to have a habit of passing it to avoid this scenario, that honestly, I don't see it unlikely to happen:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// Inside your code
class Button extends React.Component {
constructor(props) {
super(); // 😬 We forgot to pass props
console.log(props); // ✅ {}
console.log(this.props); // 😬 undefined
}
// ...
}
As per source code
function ReactComponent(props, context) {
this.props = props;
this.context = context;
}
you must pass props every time you have props and you don't put them into this.props manually.
super() is used to call the parent constructor.
super(props) would pass props to the parent constructor.
From your example, super(props) would call the React.Component constructor passing in props as the argument.
More information on super:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
For react version 16.6.3, we use super(props) to initialize state element name : this.props.name
constructor(props){
super(props);
}
state = {
name:this.props.name
//otherwise not defined
};
Here we won't get this in the constructor so it will return undefined, but we will be able to fetch this outside the constructor function
class MyComponent extends React.Component {
constructor() {
console.log(this); // Reference Error i.e return undefined
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
If we are using super(), then we can fetch the "this" variable inside the constructor as well
class MyComponent extends React.Component {
constructor() {
super();
console.log(this); // this logged to console
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
So when we are using super(); we will be able to fetch this but this.props will be undefined in the constructor. But other than constructor, this.props will not return undefined.
If we use super(props), then we can use this.props value inside the constructor as well
Sophie Alpert's Answer
If you want to use this.props in the constructor, you need to pass
props to super. Otherwise, it doesn’t matter because React sets .props
on the instance from the outside immediately after calling the
constructor.
Here is the fiddle I've made:jsfiddle.net. It shows that props are assigned not in the constructor by default. As I understand they are assinged in the method React.createElement. Hence super(props) should be called only when the superclass's constructor manually assings props to this.props. If you just extend the React.Component calling super(props) will do nothing with props. Maybe It will be changed in the next versions of React.

Categories