Does React bind this in setState callback? - javascript

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);
};

Related

Why was "this" lost? [duplicate]

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.

TypeError: _this2 is undefined REACTJS

To quickly explain my problem, in my render, I call a function that displays each element of my array via the map function.
When I click on the button, I want to send the element to another function. But when I click on the button I have the following error: "TypeError: _this2 is undefined", here are some screenshots of my code:
Thank you in advance!
Function calls in JavaScript create their own this context by default. If you apply a traditional function to a map, it creates a unique this context for each iteration when the function is called. To prevent, you can either explicitly tell the function the this context to bind itself to, or you can use arrow functions, which automatically applies the current this context to the context created.
I've created an example below to illustrate the problem. In the List component, you can see that there is a renderList function that calls a map to iteratively render the items in the list. The below code won't work, as the map function creates a context in which the renderItem function is not defined:
class List extends Component {
constructor(props) {
super(props);
}
renderList(list) {
return (
<div>
{
list.map(function(item) {
return <div>{this.renderItem(item)}</div>
})
}
</div>
)
}
renderItem(item) {
return <div style={{color: 'green'}}>{item}</div>
}
render() {
return <div>
{
this.renderList(['one', 'two', 'three'])
}
</div>
}
}
To fix this, either bind it to the current context:
renderList(list) {
return (
<div>
{
list.map(function(item) {
return <div>{this.renderItem(item)}</div>
}.bind(this))
}
</div>
)
}
OR implicitly bind the current this context arrow functions:
renderList(list) {
return (
<div>
{
list.map(item => {
return <div>{this.renderItem(item)}</div>
})
}
</div>
)
}

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.

Call method within method in reactjs

I want to call a method inside another method like this, but it is never called.
Button:
<span onClick={this.firstMethod()}>Click me</span>
Component:
class App extends Component {
constructor(props) {
super(props);
this.state = {..};
}
firstMethod = () => (event) => {
console.log("button clicked")
this.secondMethod();
}
secondMethod = () => (event) => {
console.log("this is never called!")
}
render() {..}
}
The first method is called, but not the second one. I tried to add to the constructor
this.secondMethod = this.secondMethod.bind(this);
which is recommended in all the other solutions but nothing seems to work for me. How can I call the second method correctly?
There are two problems here.
First one: You are defining your functions wrong.
firstMethod = () => (event) => {
console.log("button clicked")
this.secondMethod();
}
Like this, you are returning another function from your function. So, it should be:
firstMethod = ( event ) => {
console.log("button clicked")
this.secondMethod();
}
Second: You are not using onClick handler, instead invoking the function immediately.
<span onClick={this.firstMethod()}>Click me</span>
So, this should be:
<span onClick={() => this.firstMethod()}>Click me</span>
If you use the first version, yours, when component renders first time your function runs immediately, but click does not work. onClick needs a handler.
Here, I totally agree #Danko's comment. You should use this onClick with the function reference.
<span onClick={this.firstMethod}>Click me</span>
With this method, your function is not recreated every time your component renders since it uses your handler function with its reference. Also, no struggle writing the handler manually.
Lastly, if you define your functions as an arrow one, you don't need to .bind them.
Here is the working code.
class App extends React.Component {
firstMethod = () => {
console.log("button clicked")
this.secondMethod();
}
secondMethod = () =>
console.log("this is never called!")
// or maybe even better not using an arrow function for the
// second method since it is already bound to `this` since we
// invoke it from the firstMethod. For details, look the comments please.
/* secondMethod() {
console.log("this is never called!")
} */
render() {
return(
<span onClick={this.firstMethod}>Click me</span>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Instead of firstMethod = () => (event) try firstMethod = (event) => and instead of secondMethod = () => (event) => { try secondMethod = (event) => {
The bad news is that you can't bind arrow functions because they're lexically bound. See:
Can you bind arrow functions?
The good news is that "lexical binding" means that they should already have App as their this, i.e. it should would without explicitly binding. You'd likely redefining them as undefined, or some other odd thing by treating them this way in the constructor.
Try this, it works for me.
firstMethod = () => {
console.log("click handler for button is provided")
return (event) => this.secondMethod(event);
}
secondMethod = (event) => {
console.log("This is being called with", event);
}
Your second method returns a new function, which is redundant.
Also, your second method can be not bound, since the first method has the context already.
secondMethod = () => (event) => { ... } should be secondMethod(evnt) { ... }
Here is the working and optimized example https://codesandbox.io/s/pkl90rqmyj
Can you check this way using this url: https://codesandbox.io/s/q4l643womw
I think you're using wrong bind methods but here you can see an example
class App extends Component {
constructor() {
super();
this.state = {
num: 1,
};
this.firstMethod = this.firstMethod.bind(this);
this.secondMethod = this.secondMethod.bind(this);
}
firstMethod() {
this.secondMethod();
}
secondMethod() {
this.setState({
num: 2
});
}
render() {
return (
<div className="App">
<button onClick={this.firstMethod}>click</button>
<label>{this.state.num}</label>
</div>
);
}
}

Why is my onClick being called on render? - React.js

I have a component that I have created:
class Create extends Component {
constructor(props) {
super(props);
}
render() {
var playlistDOM = this.renderPlaylists(this.props.playlists);
return (
<div>
{playlistDOM}
</div>
)
}
activatePlaylist(playlistId) {
debugger;
}
renderPlaylists(playlists) {
return playlists.map(playlist => {
return <div key={playlist.playlist_id} onClick={this.activatePlaylist(playlist.playlist_id)}>{playlist.playlist_name}</div>
});
}
}
function mapStateToProps(state) {
return {
playlists: state.playlists
}
}
export default connect(mapStateToProps)(Create);
When I render this page, activatePlaylist is called for each playlist in my map. If I bind activatePlaylist like:
activatePlaylist.bind(this, playlist.playlist_id)
I can also use an anonymous function:
onClick={() => this.activatePlaylist(playlist.playlist_id)}
then it works as expected. Why does this happen?
You need pass to onClick reference to function, when you do like this activatePlaylist( .. ) you call function and pass to onClick value that returned from activatePlaylist. You can use one of these three options:
1. using .bind
activatePlaylist.bind(this, playlist.playlist_id)
2. using arrow function
onClick={ () => this.activatePlaylist(playlist.playlist_id) }
3. or return function from activatePlaylist
activatePlaylist(playlistId) {
return function () {
// you code
}
}
I know this post is a few years old already, but just to reference the latest React tutorial/documentation about this common mistake (I made it too) from https://reactjs.org/tutorial/tutorial.html:
Note
To save typing and avoid the confusing behavior of this, we will use
the arrow function syntax for event handlers here and further below:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => alert('click')}>
{this.props.value}
</button>
);
}
}
Notice how with onClick={() => alert('click')}, we’re passing a
function as the onClick prop. React will only call this function after
a click. Forgetting () => and writing onClick={alert('click')} is a
common mistake, and would fire the alert every time the component
re-renders.
This behaviour was documented when React announced the release of class based components.
https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html
Autobinding
React.createClass has a built-in magic feature that bound all methods to this automatically for you. This can be a little confusing for JavaScript developers that are not used to this feature in other classes, or it can be confusing when they move from React to other classes.
Therefore we decided not to have this built-in into React's class model. You can still explicitly prebind methods in your constructor if you want.
import React from 'react';
import { Page ,Navbar, Popup} from 'framework7-react';
class AssignmentDashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
}
onSelectList=(ProjectId)=>{
return(
console.log(ProjectId,"projectid")
)
}
render() {
return (
<li key={index} onClick={()=> this.onSelectList(item.ProjectId)}></li>
)}
The way you passing the method this.activatePlaylist(playlist.playlist_id), will call the method immediately. You should pass the reference of the method to the onClick event. Follow one of the below-mentioned implementation to resolve your problem.
1.
onClick={this.activatePlaylist.bind(this,playlist.playlist_id)}
Here bind property is used to create a reference of the this.activatePlaylist method by passing this context and argument playlist.playlist_id
2.
onClick={ (event) => { this.activatePlaylist.(playlist.playlist_id)}}
This will attach a function to the onClick event which will get triggered on user click action only. When this code exectues the this.activatePlaylist method will be called.

Categories

Resources