Why was "this" lost? [duplicate] - javascript

class PlayerControls extends React.Component {
constructor(props) {
super(props)
this.state = {
loopActive: false,
shuffleActive: false,
}
}
render() {
var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"
return (
<div className="player-controls">
<FontAwesome
className="player-control-icon"
name='refresh'
onClick={this.onToggleLoop}
spin={this.state.loopActive}
/>
<FontAwesome
className={shuffleClassName}
name='random'
onClick={this.onToggleShuffle}
/>
</div>
);
}
onToggleLoop(event) {
// "this is undefined??" <--- here
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
I want to update loopActive state on toggle, but this object is undefined in the handler. According to the tutorial doc, I this should refer to the component. Am I missing something?

ES6 React.Component doesn't auto bind methods to itself. You need to bind them yourself in constructor. Like this:
constructor (props){
super(props);
this.state = {
loopActive: false,
shuffleActive: false,
};
this.onToggleLoop = this.onToggleLoop.bind(this);
}

There are a couple of ways.
One is to add
this.onToggleLoop = this.onToggleLoop.bind(this); in the constructor.
Another is arrow functions
onToggleLoop = (event) => {...}.
And then there is onClick={this.onToggleLoop.bind(this)}.

Write your function this way:
onToggleLoop = (event) => {
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
Fat Arrow Functions
the binding for the keyword this is the same outside and inside the fat arrow function. This is different than functions declared with function, which can bind this to another object upon invocation. Maintaining the this binding is very convenient for operations like mapping: this.items.map(x => this.doSomethingWith(x)).

I ran into a similar bind in a render function and ended up passing the context of this in the following way:
{someList.map(function(listItem) {
// your code
}, this)}
I've also used:
{someList.map((listItem, index) =>
<div onClick={this.someFunction.bind(this, listItem)} />
)}

in my case this was the solution = () => {}
methodName = (params) => {
//your code here with this.something
}

You should notice that this depends on how function is invoked
ie: when a function is called as a method of an object, its this is set to the object the method is called on.
this is accessible in JSX context as your component object, so you can call your desired method inline as this method.
If you just pass reference to function/method, it seems that react will invoke it as independent function.
onClick={this.onToggleLoop} // Here you just passing reference, React will invoke it as independent function and this will be undefined
onClick={()=>this.onToggleLoop()} // Here you invoking your desired function as method of this, and this in that function will be set to object from that function is called ie: your component object

If you are using babel, you bind 'this' using ES7 bind operator
https://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding
export default class SignupPage extends React.Component {
constructor(props) {
super(props);
}
handleSubmit(e) {
e.preventDefault();
const data = {
email: this.refs.email.value,
}
}
render() {
const {errors} = this.props;
return (
<div className="view-container registrations new">
<main>
<form id="sign_up_form" onSubmit={::this.handleSubmit}>
<div className="field">
<input ref="email" id="user_email" type="email" placeholder="Email" />
</div>
<div className="field">
<input ref="password" id="user_password" type="new-password" placeholder="Password" />
</div>
<button type="submit">Sign up</button>
</form>
</main>
</div>
)
}
}

I want to give an explanation of why this is undefined:
If we use this in a function that is not an arrow function, this is bound to a global object when not in strict mode. But with strict mode, this will be undefined (https://www.w3schools.com/js/js_this.asp).
And ES6 modules are always in strict mode (javascript: use strict is unnecessary inside of modules).
You can bind this in onToggleLoop function with the instance of PlayerControls component by using bind method inside the constructor:
constructor(props) {
super(props)
this.state = {
loopActive: false,
shuffleActive: false,
}
this.onToggleLoop = this.onToggleLoop.bind(this)
}
Or use the arrow function instead:
onToggleLoop = (event) => {
this.setState({loopActive: !this.state.loopActive})
this.props.onToggleLoop()
}
The arrow function does not have context, so this in the arrow function will represent the object that defined the arrow function.

If you call your created method in the lifecycle methods like componentDidMount... then you can only use the this.onToggleLoop = this.onToogleLoop.bind(this) and the fat arrow function onToggleLoop = (event) => {...}.
The normal approach of the declaration of a function in the constructor wont work because the lifecycle methods are called earlier.

In my case, for a stateless component that received the ref with forwardRef, I had to do what it is said here https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd
From this (onClick doesn't have access to the equivalent of 'this')
const Com = forwardRef((props, ref) => {
return <input ref={ref} onClick={() => {console.log(ref.current} } />
})
To this (it works)
const useCombinedRefs = (...refs) => {
const targetRef = React.useRef()
useEffect(() => {
refs.forEach(ref => {
if (!ref) return
if (typeof ref === 'function') ref(targetRef.current)
else ref.current = targetRef.current
})
}, [refs])
return targetRef
}
const Com = forwardRef((props, ref) => {
const innerRef = useRef()
const combinedRef = useCombinedRefs(ref, innerRef)
return <input ref={combinedRef } onClick={() => {console.log(combinedRef .current} } />
})

You can rewrite how your onToggleLoop method is called from your render() method.
render() {
var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"
return (
<div className="player-controls">
<FontAwesome
className="player-control-icon"
name='refresh'
onClick={(event) => this.onToggleLoop(event)}
spin={this.state.loopActive}
/>
</div>
);
}
The React documentation shows this pattern in making calls to functions from expressions in attributes.

Related

React component what's the difference between callbacks implement methods

import React from 'react';
import ChildComponent from './ChildComponent';
class SampleComponent extends React.Component {
sampleCallbackOne = () => {
// does something
};
sampleCallbackTwo = () => {
// does something
};
render() {
return (
<div>
<ChildComponent
propOne={this.sampleCallbackOne}
propTwo={() => this.sampleCallbackTwo()}
/>
</div>
);
}
}
export default SampleComponent;
In this example, I have an onClick event that I am handling and saw that I can successfully pass this into the props of the component in two ways.
I was wondering what exactly the difference is in both ways since they appear to function in the same manner?
Why do both ways work?
It is a common point that seems weird.
Refer details in document of handling-events
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
handleClick() {
console.log('this is:', this);
}
<button onClick={this.handleClick}>
If you don't add () behind this.handleClick, you need to bind this in your constructor, otherwise, you may want to use the next two methods:
A. public class field syntax
which is enabled by default in Create React App
handleClick = () => {
console.log('this is:', this);
}
<button onClick={this.handleClick}>
B. arrow functions
which may cause performance problems and is not recommended, refer to the document above.
// The same on event handling but different in:
<button
onClick={(e) => this.deleteRow(id, e)} // automatically forwarded, implicitly
/>
<button
onClick={this.deleteRow.bind(this, id)} // explicitly
/>
Sample
Basically in our practice, we use public class field syntax with params which would look like below:
// No need to bind `this` in constructor
// Receiving params passed by elements as well as getting events of it
handler = (value: ValueType) => (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => {
// Do something with passed `value` and acquired `event`
}
<NumberFormat
...
onBlur={this.handler(someValue)} // Passing necessary params here
/>
We can share the handler function by passing different params to it.
// Justify via keyword of stored content in flat data structure
handler = (value: string) => (event: React.ChangeEvent<HTMLInputElement>, id: ValidationItems) => {
// Do something with
// passed `value`,
// acquired `event`,
// for each element diffenced via `id`
};
<YourComponent
id="ID_1"
value={store.name1}
onChange={this.handler("name1")}
/>;
<YourComponent
id="ID_2"
value={store.name2}
onChange={this.handler("name2")}
/>;
// ... more similar input text fields
<ChildComponent
propOne={this.sampleCallbackOne}
propTwo={() => this.sampleCallbackTwo()}
/>
for propOne: here you are passing the reference of sampleCallbackOne.
for propTwo: you are wrapping your sampleCallbackTwo in another function.
In both the case you will get the same results

ReactJS bind method to class component

Im doing ReactJS course in Codeacademny and they confused me.
(EDIT - full code) Photo of the code :
and there's no constructor or anywhere call to any bind method for the scream class method.
However in further exercises they tell you can't do that.
I probably miss something.
Apparently this.scream is an arrow function. Arrow function does not require binding. It points to the right context by default.
scream = () => { ... }
and there's no constructor or anywhere call to any bind method for the scream class method.
You only have to bind this to the component instance when the method actually uses this internally.
That's not the case in your example, so there is no need to bind it. No matter how the method is executed, it will always produce the same output.
Here is an example without React to demonstrate the difference:
var obj = {
value: 42,
method1() { // doesn't use `this`
console.log("yey!");
},
method2() { // uses `this`
console.log(this.value);
},
};
obj.method1(); // works
obj.method2(); // works
var m1 = obj.method1;
var m2 = obj.method2;
m1(); // works
m2(); // BROKEN!
var m2bound = obj.method2.bind(obj);
m2bound(); // works
scream = () => { ... }
render() {
return <button onClick={()=>this.scream()}>AAAAAH!</button>;
}
ou have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as
onClick={this.handleClick}, you should bind that method.
When you define a component using an ES6 class, a common pattern is for an event handler to be a method on the class. For example, this Toggle component renders a button that lets the user toggle between “ON” and “OFF” states:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);```
You can simply use an arrow function (no need to bind in constructor).
scream = () => { console.log('Here') }
render() {
return <button onClick={this.scream}>AAAAAH!</button>;
}
Or you can call this function inline by.
render() {
return <button onClick={() => console.log('Here')}>AAAAAH!</button>;
}
You should use arrow functions for event handling to bind the function to the object. Other solution is to auto bind each function in the constructor like :
class Test{
constructor(){
Object.getOwnPropertyNames(Test.prototype).forEach(
method => this[method] = this[method].bind(this));
}
Read about #AutoBind decorator for more details.

this is missing, in react input onChange

In the onChange function I have no this, so no props and no state what am i doing wrong ? thanks
Edit: added class and constructor!
export default class Editor extends Component {
constructor(props) {
super(props);
this.state = {
sortDirection: true,
json: props.json, // using a prop called json. were setting state.data to json
options: props.options || {}, //optional object options
id:props.id,
}
}
onChange = (e) => {
let xyz=this
/// this is undefined. needed to set state on controlled input
}
buildKeys = () => {
let keys = Object.keys(this.state.json[0]);
let self = this
return keys.map((key, index) => {
// hide column if columname in hidden columns array
/// if no hidecol option we set it an empty array
let hiddenColArr = self.state.options.hideCol || []
// loops throgh hiddenCol array and returns a bool
let isHidden = _.includes(hiddenColArr, key)
// build values
let arrIndex=this.props.id -1
let row = this.state.json[arrIndex];
return Object.keys(row).map((key2) =>
<div key={shortid.generate()} className='row' >{key}
////////////////*Input added here/
<input onChange={this.onChange} key={shortid.generate()} type="text" value={row[key2]} />
/////////////////Input end here/
</div>
)
}
When using class notation (your only choice as of React 16), you need to use arrow functions, i.e. <Thing onChange={() => this.onChange()} .../>. in order to preserve this.
If you don't, by the time onChange fires, the execution context for that call is guaranteed to not be your component, and is most likely just window.
You'll also want to change those instance properties to normal class functions:
class Thing extends Component {
constructor(props) {
super(props);
this.state = ...
}
onChange(evt) {
// do what needs to be done
}
render() {
return <div ... >
<input onChange={evt => this.onChange(evt)} ... />
</div>;
}
}
In fact, if you're using Babel + Webpack, I can almost guarantee you that's already what Babel will do to your code, so the code that runs will have normal class functions, and so you really need that arrow function as onChange handler.
(some tutorials advocate putting this.onChange = this.onChange.bind(this) in your constructor, which I would not recommend you do. It's not your constructor's job to known what the rest of the class looks like)
You can bind this to your functions by binding it in your constructor:
...
constructor(props){
super(props);
this.onChange = this.onchange.bind(this);
}
...

Does React bind this in setState callback?

I was expecting to see undefined being logged when the "Without binding" button was clicked because when you pass a method reference plainly as a callback, it shouldn't be called with the right this context, but with an arrow function, it should.
However, I saw that the callback function could access this and the value of this.state.number was logged properly. The method reference and arrow function performed the exact same. Why?
This does not have to do with arrow function class properties, it has to do with passing a reference to a method as a callback to setState as opposed to an arrow function.
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 1,
};
}
onClickWithThisBindingInCallback = () => {
this.setState({ number: 2 }, () => { this.myCallback(); });
};
onClickWithoutThisBindingInCallback = () => {
const myCb = this.myCallback;
this.setState({ number: 3 }, myCb);
};
myCallback() {
console.log(this.state.number);
}
render() {
return (
<div className="App">
<button onClick={this.onClickWithThisBindingInCallback}>With binding</button>
<button onClick={this.onClickWithoutThisBindingInCallback}>Without binding</button>
</div>
);
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
<script src="https://unpkg.com/react#16.2.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.2.0/umd/react-dom.development.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
React calls the callback function of setState with the component's instance this, as seen by this line of code.
Credits: #Li357
It is because you are given a lambda function to it:
onClickWithThisBinding = () => {
A lambda function is executed in it's given context. So the binding is actually onClick={() => { .. }}. That is why it has the this context.
This is due to the use of the arrow function.
Arrow functions always have the same this of their surrounding code.
See more here.
If you want the method not to be bound to that specific class you can use a normal function like this.
onClickWithoutThisBinding() {
const myCb = this.myCallback;
this.setState({ number: 3 }, myCb);
};

'this' is undefined in React component function

When I try to execute the below code, the value of this within the function renderContents is undefined.
I had thought that fat arrow functions bound this automatically. Is this not the case? If not, how can I ensure that this is passed to the function renderContents?
Code sample:
class Box extends React.Component {
renderContents = () => {
console.log(this); // undefined
return (
<div></div>
)
}
render() {
const {
someValue,
} = this.props;
return (
<div>
{someValue ? this.renderContents() : null}
</div>
);
}
}
There is nothing wrong. I just called it like this:
ReactDOM.render((<Box someValue={true} />), document.getElementById('content'));
and I got the value of this:
So I think the problem would be, you aren't calling it right, are you setting someValue??
You make mistake in renderContent method. You declare it as function and it have no context of your component.
Please try to declare methods like this:
renderContent() {
...your code...
}
Instead this:
renderContent = () => {}
Have you tried to add an constructor method?
constructor(props) {
super(props)
}
EDIT:
Instead of using arrow functions, you could bind the context in constructor like this:
class Box extends React.Component {
constructor(props) {
super(props)
this.renderContents = this.renderContents.bind(this)
}
renderContents() {
console.log(this);
return (
<div></div>
)
}
render() {
const {
someValue,
} = this.props;
return (
<div>
{someValue ? this.renderContents() : null}
</div>
);
}
}
In react classes just some methods are implicitly binded (render, componentDidMount, componentWillUnmount, etc.), other methods you have to bind manually in constructor or use arrow functions.

Categories

Resources