When we translated the previous code to ES2015 syntax, some functions got converted to a different syntax. Some of them are funcName() and some of them are funcName = () =>. What's the difference?
import React, { Component, PropTypes } from 'react';
export default class Stopwatch extends Component {
state = {
running: false,
previouseTime: 0,
elapsedTime: 0,
}
componentDidMount() {
this.interval = setInterval(this.onTick);
}
componentWillUnmount() {
clearInterval(this.interval);
}
onStart = () => {
this.setState({
running: true,
previousTime: Date.now(),
});
}
onStop = () => {
this.setState({
running: false,
});
}
onReset = () => {
this.setState({
elapsedTime: 0,
previousTime: Date.now(),
});
}
onTick = () => {
if (this.state.running) {
var now = Date.now();
this.setState({
elapsedTime: this.state.elapsedTime + (now - this.state.previousTime),
previousTime: Date.now(),
});
}
}
render() {
var seconds = Math.floor(this.state.elapsedTime / 1000);
return (
<div className="stopwatch" >
<h2>Stopwatch</h2>
<div className="stopwatch-time"> {seconds} </div>
{ this.state.running ?
<button onClick={this.onStop}>Stop</button>
:
<button onClick={this.onStart}>Start</button>
}
<button onClick={this.onReset}>Reset</button>
</div>
)
}
}
funcName() in a JavaScript class will add a function to the prototype. These functions will exist only once, attached to the prototype. Without the class syntax you would define a prototype function as follows:
Stopwatch.prototype.funcName = function() { /* ... */ };
The other functions are actually created as properties that contain anonymous functions that exist once per each instance and are created in the constructor when the class is instantiated. This is equivalent to creating them in the constructor as below:
class Stopwatch /* ... */ {
constructor() {
this.onStart = () => { /* ... */ };
}
}
The reason for doing this is that using the => syntax to create the functions causes the functions to be permanently bound to the this value of each instance. Therefore they can be passed around to other things (such as set as event handlers) and when they are called, this will always point to the correct object inside the function.
Whether this is a good idea or just plain unreadable/too tricky is perhaps a matter of taste.
Related
This is what I do when I want to make this of my class functions bind to the class (component) instance. Is there any easier/more proper way to do this in React?
class App extends Component {
state = {
currentSection: 1,
message :{text:''}
};
constructor(props) {
super(props);
this.prevSection = this.prevSection.bind(this);
this.nextSection = this.nextSection.bind(this);
this.mobileChanged = this.mobileChanged.bind(this);
}
}
If I recall correctly, if you change:
function nextSection() {...}
to
const nextSection = () => {...}
After this change, you can remove this and the bind
Please let me know if your component will remain as functional like it was before. I'm not sure if it this will change the behaviour.
You could use arrow function instead of class method
With arrow function, there will be no this context so you won't have to bind it
class App extends Component {
state = {
currentSection: 1,
message: { text: '' },
};
prevSection = () => {}
nextSection = () => {}
mobileChanged = () => {}
}
Live example:
I am trying to render a class component using conditional rendering. The class component has a state value that is by default set to false. In the class, a function called start() performs some operation and based on the results, it will call a function verifyTheIdentity() that has setState(). But I am not able to call the verifyTheIdentity() from start().
import React from "react";
import * as faceapi from "face-api.js";
import web3 from "../web3";
class recognize extends React.Component {
constructor(props) {
super();
this.state = {
verified: false
}
this.verifyTheIdentity = this.verifyTheIdentity.bind(this);
// this.start = this.start.bind(this);
}
verifyTheIdentity() {
this.setState(prevState => {
return {
verified: true
}
})
}
async start() {
///////////////////CODE FOR FACE RECOGNITION///////////////////////////////
let name = result.toString();
console.log(name);
const pattern = new RegExp(/^unknown/)
console.log(pattern.test(name))
if (pattern.test(name) === false) {
document.getElementById("verifier").disabled = false;
document.getElementById("verifier").onClick = this.verifyTheIdentity; //This is where I am going wrong.
}
else
document.getElementById("verifier").disabled = true;
})
})
}
render() {
return (
<div>
{this.state.verified == false ? <div><br /><br /><br /><br />
<input type="file" id="imageUpload" /><br />
<button disabled id="verifier" >Verify</button></div> :
<div><br /><br /><br /><br /><p>Verified</p></div>}
</div>
)
}
}
export default recognize;
And this is the error I get.
Unhandled Rejection (TypeError): Cannot read property
'verifyTheIdentity' of undefined.
First of all, you do not need bind at all, if you use arrow function.
constructor(props) {
super();
this.state = {
verified: false
}
}
verifyTheIdentity = () => {
//code
}
start = async () => {
// code
}
Try this, it should work.
or
remove commented code,
add in constructor (remove // from the line)
this.start = this.start.bind(this);
you can replace this line by below one
document.getElementById("verifier").onClick = this.verifyTheIdentity;
like this
document.getElementById("verifier").onClick = this.verifyTheIdentity();
The issue is related to Javascript Syntax violation -
"Calling a function without parenthesis"
To fix the issue, either call the function verifyTheIdentity() with proper syntax as:
document.getElementById("verifier").onClick = this.verifyTheIdentity();
Or, assign the function to a varialbe and call it:
const test = verifyTheIdentity() {
this.setState(prevState => {
return {
verified: true
}
})
};
document.getElementById("verifier").onClick = this.test ;
For your understanding, see the example:
function Test(){
alert(1);
}
//Test; //It won't work
Test(); // It will work
I have a pretty straightforward problem, but I can't see to figure out why it happens. I have a component in which I declare two arrays in my constructor:
class FilterModal extends Component {
constructor(props) {
super(props);
this.transport_options = ["driving", "tram", "walking"];
this.pressed_percentages = ["allPressed", "under15Pressed","under30Pressed", "over30Pressed"];
filters = {
"driving": {
"allPressed":false,
"under15Pressed":false,
"under30Pressed":false,
"over30Pressed":false
},
"tram": {
"allPressed":false,
"under15Pressed":false,
"under30Pressed":false,
"over30Pressed":false
},
"walking": {
"allPressed":false,
"under15Pressed":false,
"under30Pressed":false,
"over30Pressed":false
},
"isFilterActive": false
}
//state declared here
}
}
I want to access the variables transport_options and pressed_percentages in a function that I define after the constructor:
resetPressed = () => {
this.transport_options.forEach(function (transport_option) {
this.pressed_percentages.forEach(function (pressed_percentage) {
filters[transport_option][pressed_percentage] = false;
})
});
//additional business logic
}
My problem is this: when I call resetPressed, I get the message "undefined is not an object (evaluating 'this.pressed_percentages'). However, this.transport_options does not trigger any error message.
So my question is: why does this.transport_options work, but this.pressed_percentages throws an error?
Can you also share the code where you have called this function?
Looks like this scope is changed
This must be the issues, in which your function is not binded. try using arrow operator to define your function or use bind function as it is used in following example.
class App extends React.Component {
constructor(props) {
super(props);
this.transport_options = ["driving", "tram", "walking"];
this.pressed_percentages = ["allPressed", "under15Pressed","under30Pressed", "over30Pressed"];
this.filters = {
"driving": {
"allPressed":false,
"under15Pressed":false,
"under30Pressed":false,
"over30Pressed":false
},
"tram": {
"allPressed":false,
"under15Pressed":false,
"under30Pressed":false,
"over30Pressed":false
},
"walking": {
"allPressed":false,
"under15Pressed":false,
"under30Pressed":false,
"over30Pressed":false
},
"isFilterActive": false
}
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.transport_options)
}
render() {
return (
<button onClick={this.handleClick}>
click me
</button>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
In callbacks also you have to bind your functions, like this.
resetPressed = () => {
this.transport_options.forEach((transport_option)=> {
this.pressed_percentages.forEach((pressed_percentage) =>{
this.filters[transport_option][pressed_percentage] = false;
})
});
//additional business logic
}
For more explanation please refer event handling react
I tried to call a function in set time inter val for every 5 seconds but i throws errors in
TypeError: this.intialState is not a function
componentDidMount() {
this.intialState();
setInterval(this.changeSelection,5000);
}
changeSelection(){
this.intialState();
}
TypeError: this.intialState is not a function
Updated 5-second countdown using class Clock extends Component
import React, { Component } from 'react';
class Clock extends Component {
constructor(props){
super(props);
this.state = {currentCount: 10}
}
timer() {
this.setState({
currentCount: this.state.currentCount - 1
})
if(this.state.currentCount < 1) {
clearInterval(this.intervalId);
}
}
componentDidMount() {
this.intervalId = setInterval(this.timer.bind(this), 1000);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
render() {
return(
<div>{this.state.currentCount}</div>
);
}
}
export default Clock;
this loses its context inside functions. You can bind changeSelection in the constructor
constructor() {
super();
this.changeSelection = this.changeSelection.bind(this);
setInterval(this.changeSelection, 500);
}
or make it a fat arrow function since these functions don't have their own this context and will take the parent's
changeSelection = () => {
// code here
}
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this
componentDidMount() {
this.intialState();
setInterval(this.changeSelection,5000);
}
changeSelection = () => {
this.intialState();
}
The problem is that your function 'changeSelection' does not have access to 'this'.
There are two easy ways to solve this issue:
In your 'constructor', add this line to bind 'this' with changeSelection
this.changeSelection = this.changeSelection.bind()
An arrow function
changeSelection = () => {};
Click here to see more ways to do binding
You can read more about why we need binding why and how to bind
My problem is described in the topic
Here it works:
handleItemClick = (e, { name }) => {
if (name !== this.props.prevName) {
document.getElementById(name).style = 'border: 3px solid black';
if (document.getElementById(this.props.prevName))
document.getElementById(this.props.prevName).style = 'border: 1px solid black';
this.props.dispatch({type: 'CHANGE_PREVNAME', payload: name});
let i = this.props.items.findIndex(element => {
return (element.general.firstName + ' ' + element.general.lastName) === name;
});
this.props.dispatch( { type: 'CHANGE_SELECTION', payload: this.props.items[i] } );
}
}
And here it doesn't work:
searchHandler(event) {
this.props.dispatch( { type: 'CHANGE_TERM', payload: event.target.value } );
}
It's functions of the same class, and here mapDispatchToProps (outside the class ofc) func:
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators({
toTakeData: loadItems,
dispatch: dispatch
}, dispatch)
};
}
From the react docs:
You 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.
If you use babel you can use the public class fields syntax which will lead to this being bound automatially. Note that this method is still not in the language standard and does only work because babel transforms it to valid javascript:
searchHandler = event => { /* this is defined here ... */ }
The es5 way would be to bind the function in the constructor:
class MyComponent extends Component {
constructor(props) {
super(props);
// bind this to your handler
this.searchHandler = this.searchHandler.bind(this);
/* other initialization */
}
searchHander(event) { /* you can access this here... */ }
}
Note that the arrow function syntax comes with some limitations. For example you can't override it in classes that extend the class it was defined in. In react this is not a problem most of the time because inheritance is discouraged in favour of composition.
For the function it is not working, make it an arrow function
searchHandler = (event) => { ... }
searchHandler(event) {
this.props.dispatch( { type: 'CHANGE_TERM', payload: event.target.value } );
}
Problem is of "this". "this" for searchHandler is not bound correctly. For handleItemClick function as it is defined as arrow function and arrow function get "this" from where it is defined.