Arrow function ambguity in ReactJS - reactjs

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
In the above code - I wasn't able to understand the setInterval() line - to be precise the function argument of the setInterval line. I think it is an arrow function- I might be wrong. I replaced it with a regular function setInterval(function(){this.tick()},1000) and got an error saying tick() is not a function. What's happening here?

The this reference is reset when using old-style function() syntax, whereas with => (arrow-functions) the this reference is preserved. You can still use function() but you need to call .bind(this) to "fix" the this reference.
So this:
this.timerID = setInterval(
() => this.tick(),
1000
);
Is equivalent to this:
this.timerID = setInterval(
function() { this.tick(); }.bind(this),
1000
);
You need to do this because setTimeout/setInterval is a member-property of the global (window) object, so inside a setTimeout or setInterval callback the this reference is for window, not the call-site's this.

Related

How can I define a method inside a class component in React?

I have made a simple digital clock in React. It seems working. However, I wanted to define the callback function inside the setState() separately. But I got an error. Do you know how I can define such a function called tick() outside the componenDidMount? Below is my code
import "./Clock.css";
class Clock extends React.Component {
state = { date: new Date() };
componentDidMount() {
setInterval(() => {
this.setState({ date: new Date() });
}, 1000);
console.log("componentdidmount");
}
render() {
return (
<div className="clock-container">
<h1 className="clock">{this.state.date.toLocaleTimeString()}</h1>
</div>
);
}
}
export default Clock;
Is this what you want it to be like?
tick(){
this.interval = setInterval(() => {
this.setState({ date: new Date() });
}, 1000);
}
componentDidMount() {
this.tick();
console.log('componentdidmount');
}
componentWillUnmount(){
clearInterval(this.interval);
}

TypeError: this is undefined?

I cannot reach this.state or I cannot setState inside the componentDidMount function. Why?
import React from 'react';
class LoginApi extends React.Component {
constructor(props) {
super(props);
this.state = {
formNames: [],
};
}
componentDidMount() {
let apiKey = '###';
window.JF.initialize({ apiKey: apiKey });
window.JF.login(
function success() {
window.JF.getForms(function(response) {
for (var i in response) {
this.setState({ formNames: [...this.state.formNames, response[i]] });
}
});
},
function error() {
window.alert('Could not authorize user');
}
);
}
render() {
return (
<div className="initializeForm">
<ul>
{this.state.formNames.map(forms => (
<li key={forms}>{forms}</li>
))}
</ul>
</div>
);
}
}
export default LoginApi;
When I have tried to this code I am getting TypeError: this is undefined.
this.setState({formNames: [...this.state.formNames,response[i]]});
Because you are referencing this within another function. When you create your callback function in JF.login and again in JF.getForms with the function keyword, it creates a new this context, hence why setState is undefined.
A common way to fix this is by using an arrow function (=>), which will end up using the this from its enclosing lexical scope, which in your case is the class.
window.JF.login(
() => {
window.JF.getForms((response) => {
for (var i in response) {
this.setState({ formNames: [...this.state.formNames, response[i]] });
}
});
},
() => {
window.alert('Could not authorize user');
}
);
Because you write the success function without bind / not as an arrow function. Without it, this refers to the calling function. In this case, to the success function. Change it to an arrow function and it will work. () => {...}

How to change interval value in React

I've tried changing the interval value when using ReactJs, but can't seem to get it to work. I've looked it up but haven't found anything useful (or maybe I didn't get to it while reading). I have most of my code inside a class component.
My code looks like this:
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
tickS: 1000,
...
};
}
componentDidMount() {
this.intervalID = setInterval(
() => this.tick(),
this.state.tickS
);
}
tick(){
this.setState({
...
tickS: 10**Math.log10(this.state.tickS + this.state.clicksChange),
...
});
}
}
It does work for using 1000 milliseconds, but when i try to redefine the state of tickS, it doesn't work.
You can't change the interval of setInterval. Instead, you can take another approach and use setTimeout for the same purpose. Check out the example:
class Timer extends React.Component {
constructor(props) {
super(props);
this.ticks = 1000;
}
componentDidMount() {
this.timeoutID = setTimeout(
() => this.tick(),
this.tick
);
}
tick() {
this.setState({
...
});
this.ticks = 10 ** Math.log10(this.ticks + this.state.clicksChange),
this.timeoutID = setTimeout(
() => this.tick(),
this.ticks
);
}
}

ReactJS How do i call setState nested Functions

Lets say that i have this component:
export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
test: false
};
}
func1 = () => {
function update() {
this.setState({ test: true }); // not working
}
};
render() {
return <div />;
}
}
As you see i have func1 which is in arrow form,and there is another function update in function update() form
So how do i call setState from inside function update as seen in example ?
EDIT:
the reason why i am trying to do something like this is i am using a game engine inside react component called phaser.So actually if i make update function as an arrow function for some reason phaser cant understand it and throws undefined error.update function is called 60 times in second
export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
test: false
};
}
componentDidMount(){
this.func1()
}
func1 = () => {
var game = new Phaser.Game(canvas_width,canvas_height,Phaser.AUTO,'gameDiv',{ preload: preload, create: create, update: update });
function update() {
this.setState({ test: true }); // not working
}
};
render() {
return <div />;
}
}
SECOND EDIT:
THANKS GUYS WRITING update = update.bind(this) before var game solved it
You can use an arrow function again:
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
test: false
};
}
func1 = () => {
const update = () => {
this.setState({ test: true }) // not working
}
return update;
}
componentDidMount() {
this.func1()();
}
render() {
console.log( this.state);
return (
<div></div>
)
}
}
ReactDOM.render(<Test />, document.getElementById("root"));
<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>
When you use a regular function you lose the scope of this. When you use an arrow function you can use it without worrying about this as you do in your first function. But, instead of an arrow function that would work:
func1 = () => {
const that = this;
return function update() {
that.setState({ test: true }); // not working
}
}
Or even with bind as #Tholle suggested in his comment:
func1 = () => {
return ( function update() {
this.setState({ test: true }); // not working
}).bind(this);
}
Furthermore, you can define them separately and call the update function inside the func1.
func1 = () => {
this.update();
};
update() {
this.setState({ test: true }); // not working
}
This works too, because you are auto-binding your func1 function to this and your update function is invoked from there, keepin this scope.
you can pass this in as an argument, perhaps calling it self within the function. I.e self.setState. You could also bind or call the function passing the this as an argument. The important thing is that this still references the correct React component.
also, I'm a little confused why you're defining a function which returns a function like that. Could you just pass the function in point free form instead and not need to worry about this issue? You still need to bind the function to this..
export default class Test extends React.Component {
constructor(props) {
super(props);
this.func1 = this.func1.bind(this)
this.state = {
test: false
};
}
func1() {
this.setState({ test: true });
};
render() {
return <div callback={this.func1} />;
}
}

Element besides another

I am trying to display the time next to the text "Market Data" at the top header of the homepage. Tick is defined by the tick function below. The interval is set to one second.
The text "Market Data" gets displayed fine, but the time is not there.
var HomePage = React.createClass({
getInitialState: function() {
setInterval(this.tick, 1000);
},
tick : function() {
const element = (
<div>
<h1>{new Date().toLocaleTimeString()}.</h1>
</div>
},
render: function () {
return (
<div>
<div className="row">
<center>{this.tick}</center>
<center><p style={{ color:'blue', fontSize:'25px', fontWeight:'bold'}}>Market Data</p></center>
<StockTable stocks={this.state.stocks} last={this.state.last} />
</div>
</div>
);
}
});
React.render(<HomePage />, document.getElementById('main'));
I have similar code running. You need to trigger the component to update every tick, you'll do this by setting a new state after the timer. Setting a state triggers the component to update, but before you do, you remove the timer using componentWillUnmount(). When the updated component mounts, componentDidMount() will trigger and you set a new timer. It's something like an infinite cycle where each action triggers the next. Here's the code I'm using, it uses ES6 so you'll have to adapt it a bit.
constructor(props) {
super(props);
this.state = {
time: 0
};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.state = { time:new Date() }
//The action you want to execute at every tick.
}
render(){
return(
<div>The time: {this.state.time}</div>
);
}
Here's the official explanation for this from React documentation. https://facebook.github.io/react/docs/state-and-lifecycle.html
Do this and you are good to go. The idea is that React updates the component when you change the state of the component. So, on certain interval, you are changing the state which leads to React rendering the change which is new time.
var HomePage = React.createClass({
componentWillMount: function(){
this.state = {
timeNow: new Date().toLocaleTimeString()
}
},
componentDidMount: function() {
this.timer = setInterval(this.tick.bind(this), 1000);
},
componentWillUnmount() {
clearInterval(this.timer);
},
tick : function() {
this.setState({
timeNow: new Date().toLocaleTimeString()
})
},
render: function () {
return (
<div>
<div className="row">
<center>
<div>
<h1>{this.state.timeNow}</h1>
</div>
</center>
<center><p style={{ color:'blue', fontSize:'25px', fontWeight:'bold'}}>Market Data</p></center>
</div>
</div>
);
}
});
React.render(<HomePage />, document.getElementById('main'));
Working example: https://jsfiddle.net/69z2wepo/73851/

Resources