ReactJS: Warning: setState(...): Cannot update during an existing state transition - reactjs

I am trying to refactor the following code from my render view:
<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange.bind(this,false)} >Retour</Button>
to a version where the bind is within the constructor. The reason for that is that bind in the render view will give me performance issues, especially on low end mobile phones.
I have created the following code, but I am constantly getting the following errors (lots of them). It looks like the app gets in a loop:
Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
Below is the code I use:
var React = require('react');
var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
var Button = require('react-bootstrap/lib/Button');
var Form = require('react-bootstrap/lib/Form');
var FormGroup = require('react-bootstrap/lib/FormGroup');
var Well = require('react-bootstrap/lib/Well');
export default class Search extends React.Component {
constructor() {
super();
this.state = {
singleJourney: false
};
this.handleButtonChange = this.handleButtonChange.bind(this);
}
handleButtonChange(value) {
this.setState({
singleJourney: value
});
}
render() {
return (
<Form>
<Well style={wellStyle}>
<FormGroup className="text-center">
<ButtonGroup>
<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange(false)} >Retour</Button>
<Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChange(true)} >Single Journey</Button>
</ButtonGroup>
</FormGroup>
</Well>
</Form>
);
}
}
module.exports = Search;

Looks like you're accidentally calling the handleButtonChange method in your render method, you probably want to do onClick={() => this.handleButtonChange(false)} instead.
If you don't want to create a lambda in the onClick handler, I think you'll need to have two bound methods, one for each parameter.
In the constructor:
this.handleButtonChangeRetour = this.handleButtonChange.bind(this, true);
this.handleButtonChangeSingle = this.handleButtonChange.bind(this, false);
And in the render method:
<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChangeSingle} >Retour</Button>
<Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChangeRetour}>Single Journey</Button>

I am giving a generic example for better understanding, In the following code
render(){
return(
<div>
<h3>Simple Counter</h3>
<Counter
value={this.props.counter}
onIncrement={this.props.increment()} <------ calling the function
onDecrement={this.props.decrement()} <-----------
onIncrementAsync={this.props.incrementAsync()} />
</div>
)
}
When supplying props I am calling the function directly, this wold have a infinite loop execution and would give you that error, Remove the function call everything works normally.
render(){
return(
<div>
<h3>Simple Counter</h3>
<Counter
value={this.props.counter}
onIncrement={this.props.increment} <------ function call removed
onDecrement={this.props.decrement} <-----------
onIncrementAsync={this.props.incrementAsync} />
</div>
)
}

That usually happens when you call
onClick={this.handleButton()} - notice the () instead of:
onClick={this.handleButton} - notice here we are not calling the function when we initialize it

THE PROBLEM is here: onClick={this.handleButtonChange(false)}
When you pass this.handleButtonChange(false) to onClick, you are actually calling the function with value = false and setting onClick to the function's return value, which is undefined. Also, calling this.handleButtonChange(false) then calls this.setState() which triggers a re-render, resulting in an infinite render loop.
THE SOLUTION is to pass the function in a lambda: onClick={() => this.handleButtonChange(false)}. Here you are setting onClick to equal a function that will call handleButtonChange(false) when the button is clicked.
The below example may help:
function handleButtonChange(value){
console.log("State updated!")
}
console.log(handleButtonChange(false))
//output: State updated!
//output: undefined
console.log(() => handleButtonChange(false))
//output: ()=>{handleButtonChange(false);}

If you are trying to add arguments to a handler in recompose, make sure that you're defining your arguments correctly in the handler. It is essentially a curried function, so you want to be sure to require the correct number of arguments. This page has a good example of using arguments with handlers.
Example (from the link):
withHandlers({
handleClick: props => (value1, value2) => event => {
console.log(event)
alert(value1 + ' was clicked!')
props.doSomething(value2)
},
})
for your child HOC and in the parent
class MyComponent extends Component {
static propTypes = {
handleClick: PropTypes.func,
}
render () {
const {handleClick} = this.props
return (
<div onClick={handleClick(value1, value2)} />
)
}
}
this avoids writing an anonymous function out of your handler to patch fix the problem with not supplying enough parameter names on your handler.

The problem is certainly the this binding while rending the button with onClick handler. The solution is to use arrow function while calling action handler while rendering. Like this:
onClick={ () => this.handleButtonChange(false) }

From react docs Passing arguments to event handlers
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

This same warning will be emitted on any state changes done in a render() call.
An example of a tricky to find case:
When rendering a multi-select GUI component based on state data, if state has nothing to display, a call to resetOptions() is considered state change for that component.
The obvious fix is to do resetOptions() in componentDidUpdate() instead of render().

I got the same error when I was calling
this.handleClick = this.handleClick.bind(this);
in my constructor when handleClick didn't exist
(I had erased it and had accidentally left the "this" binding statement in my constructor).
Solution = remove the "this" binding statement.

The onClick function must pass through a function that returns the handleButtonChange() method. Otherwise it will run automatically, ending up with the error/warning. Use the below to solve the issue.
onClick={() => this.handleButtonChange(false)}

The solution that I use to open Popover for components is reactstrap (React Bootstrap 4 components).
class Settings extends Component {
constructor(props) {
super(props);
this.state = {
popoversOpen: [] // array open popovers
}
}
// toggle my popovers
togglePopoverHelp = (selected) => (e) => {
const index = this.state.popoversOpen.indexOf(selected);
if (index < 0) {
this.state.popoversOpen.push(selected);
} else {
this.state.popoversOpen.splice(index, 1);
}
this.setState({ popoversOpen: [...this.state.popoversOpen] });
}
render() {
<div id="settings">
<button id="PopoverTimer" onClick={this.togglePopoverHelp(1)} className="btn btn-outline-danger" type="button">?</button>
<Popover placement="left" isOpen={this.state.popoversOpen.includes(1)} target="PopoverTimer" toggle={this.togglePopoverHelp(1)}>
<PopoverHeader>Header popover</PopoverHeader>
<PopoverBody>Description popover</PopoverBody>
</Popover>
<button id="popoverRefresh" onClick={this.togglePopoverHelp(2)} className="btn btn-outline-danger" type="button">?</button>
<Popover placement="left" isOpen={this.state.popoversOpen.includes(2)} target="popoverRefresh" toggle={this.togglePopoverHelp(2)}>
<PopoverHeader>Header popover 2</PopoverHeader>
<PopoverBody>Description popover2</PopoverBody>
</Popover>
</div>
}
}

Related

How to solve the lost binding issue for event handlers in React?

In React, it looks like for a self-contained component, we are still experiencing the "lost binding issue":
The following React code in CodePen to change a from 123 to 456 doesn't work:
class Foo extends React.Component {
state = { a: 123 };
clickHandler() {
this.setState({ a: 456 });
}
render() {
return (
<div>
<h1>Hello, world! {this.state.a} </h1>
<button onClick={this.clickHandler}>Click Me</button>
</div>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById('root')
);
It can be solved by
making it onClick={this.clickHandler.bind(this)}
making the handler an arrow function
Is there any other way to write the code to handle the lost binding issue or to make the issue go away altogher? Is it possible to do in the constructor or componentDidMount() to bind all the methods to itself, something like (pseudo code) so that the issue of lost binding can be gone as far as the component is concerned?
for (methodName of this.allMethodNames()) {
this[methodName] = this[methodName].bind(this);
}
If you want automatic binding you'll need to rewrite the onClick to something like this:
<button onClick={() => this.clickHandler()}>Click Me</button>
Otherwise you'll have to do it like this:
<button onClick={this.clickHandler.bind(this)}>Click Me</button>
One way around both of those is to use functional components where you could just pass the function with no issues. Here is your component turned into a functional component here: https://codesandbox.io/s/distracted-nobel-46wgf
import React, { useState } from "react";
import ReactDOM from "react-dom";
export default function Foo() {
const [state, setState] = useState({ a: 123 });
const clickHandler = () => {
setState({ ...state, a: 456 });
};
return (
<div>
<h1>Hello, world! {state.a} </h1>
<button onClick={clickHandler}>Click Me</button>
</div>
);
}
ReactDOM.render(<Foo />, document.getElementById("root"));
Your components needs a constructor, that's where binding is supposed to happen in class-based React:
class Foo extends React.Component {
// Set up your constructor and explicitly pass props in
// React will pass props in automatically but this just makes it easier to read
constructor(props) {
// `this` points to `Foo` now that you have a constructor
// it will also make the state obj available to your custom methods
this.state = { a: 123 };
// inside of your constructor, bind your custom methods to `this`
// if you have more than one, bind them all here
this.clickHandler = this.clickHandler.bind(this)
}
clickHandler() {
this.setState({ a: 456 });
}
render() {
return (
<div>
<h1>Hello, world! {this.state.a} </h1>
<button onClick={this.clickHandler}>Click Me</button>
</div>
);
}
}
You can use ES6 arrow functions. They prevent you from having to call .bind(this) for every function.
clickHandler = () => {
this.setState({ a: 456 });
}
<button onClick={this.clickHandler}>Click Me</button>
I believe there's no way to automatically bind every function you have to bind, simply because you are calling another component whenever you use onClick which makes your this won't work anymore (e.g. you have used a button in your example).
However, there are two other ways, you can either bind it in the constructor (similar to what you suggested to bind it in componentDidMount() but that won't work), like this:
constructor( props ){
super( props );
this.clickHandler = this.clickHandler.bind(this);
}
Or you can either change your function to an arrow function if you do not want to use an arrow function in render method like this onClick={()=>clickHandler()}, which is no good because you'll create a new function instance every time render is called :
clickHandler = () => {
this.setState({ a: 456 });
}
<button onClick={this.clickHandler}>Click Me</button>
I personally recommend the second way because you don't have to write one more line for the binding. More importantly, you can pass extra params directly from the clickHandler like this:
clickHandler = (addMore, addEvenMore) => () => {
this.setState({ a: 456 + addMore + addEvenMore });
// set a to 456 + 100 + 200
}
<button onClick={this.clickHandler(100, 200)}>Click Me</button>
With a binding in constructor, you cannot pass parameters, you have to do the binding when you pass the function to button, making the line clumsy, because the handler now describe multiple actions (the onClick action and the bind action) with the existence of the word bind:
<button onClick={this.clickHandler.bind(this, 100, 200)}>Click Me</button>
You can bind in the constructor. However, if you want "auto binding", I'd recommend using arrow functions since they inherit the lexical scope of this -- where this refers to the parent class versus an unbinded callback method that loses its lexical scope and this refers to the window.
In addition, I'd avoid binding this in the callback function because React has to recreate the function for each re-render (which can lead to an ever so slightly slower performance).
That said, you could do something like the example below -- although I don't recommend it, since not all of these methods would need to be binded to this if they weren't used as a callback in the render method (also Object.getOwnPropertyNames is not supported in older browsers):
import React from "react";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = { a: 123 };
Object.getOwnPropertyNames(App.prototype)
.filter(method => !["constructor", "render"].includes(method))
.forEach(method => {
this[method] = this[method].bind(this);
});
}
handleSetClick() {
this.setState({ a: 456 });
}
handleResetClick() {
this.setState({ a: 123 });
}
render() {
return (
<div>
<h1>Hello, world! {this.state.a} </h1>
<button type="button" onClick={this.handleSetClick}>
Click Me
</button>
<br />
<button type="button" onClick={this.handleResetClick}>
Reset
</button>
</div>
);
}
}
export default App;
Working example:
What it looks like to "auto" bind (compiled):
https://pastebin.com/dZE0Hdnn
What it looks like to use arrow functions (compiled):
https://pastebin.com/e0xmh1fn
The main difference is that arrow functions instantiate the class versus being bound as a prototype to the class when bound in the constructor. This mostly affects inheritance, which you can read more about here. That said, React components don't usually extend from plain classes, instead they either extend from React's Component or PureComponent, and you won't be directly calling a Class.prototype, so take the article with a grain of salt (also, the article is very old and the performance numbers are outdated).
On a side note, you also bind using decorators, although support for decorators is hit or miss (since they're still in proposal stage -- or were, I haven't checked in awhile).

How to correctly initialize a function in React?

tell me, please, how to solve the following problem correctly?
I have a certain component, there is a control above, when I click on it, setState is triggered. I need to call the function this.setScrollLeft () in which I set to the selected node (ref) in this case the cleavage position.
Here is my implementation, but I am sure that there is a better solution:
import React from 'react';
import { ScoreCell, getScoreTheme } from 'components/scores';
class LeaderboardPlayerResult extends React.Component {
constructor(props) {
super(props);
this.containerWidth = 198;
this.data = this.props.data;
this.playerResultRef = React.createRef();
}
componentDidMount() {
this.element = this.playerResultRef.current;
this.element.scrollLeft = this.containerWidth;
}
setScrollLeft = () => {
if (this.element) {
this.element.scrollLeft = this.containerWidth;
}
};
playerResult = () => {
if (this.data.playOffHoles) {
return (
this.data.playOffHoles.map((item, index) => {
return (
<div
className="leaderboard__player-result-row-wrapper"
key={index}
>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holeId}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell">{item.holePar}</div>
</div>
<div className="leaderboard__player-result-row">
<div className="leaderboard__player-result-cell leaderboard__player-result-cell--score">
<ScoreCell
childCss='tee-times-card__score'
theme={getScoreTheme(item.playOffParScore)}
>{item.playOffParScore}</ScoreCell>
</div>
</div>
</div>
);
})
);
}
};
render() {
console.log('LeaderboardPlayerResult render');
this.setScrollLeft();
return (
<div
className="leaderboard__player-result"
ref={this.playerResultRef}
>
{this.playerResult()}
</div>
);
}
}
The best place to put this.setScrollLeft() is inside the componentDidUpdate method.
You are already calling this method (this.setScrollLeft()) inside componentDidMount, what is right. Now, you could put another call into componentDidUpdate and it will work pretty much as it is working by now because componentDidUpdate is called before render.
The final outcome will be the same, however, you are separating the concerns: render only render the components and the other methods deal with your business logic.
If you are not sure about componentDidMount and componentDidUpdate, see these excerpts from the official React.js documentation:
componentDidMount()
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
componentDidUpdate()
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.

React event handler works without bind()

I'm working through a react tutorial and the instructor is showing that the event handler in this code won't work, because this() is accessing the outer environment. But I get no error. Can someone explain it to me?
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0,
};
handleIncrement() {
console.log(this.state);
console.log(this.props);
}
render() {
return (
<div>
<button onClick={this.handleIncrement()}>
Increment
</button>
</div>
);
}
}
export default Counter;
The thing is, when your event handler needs to access this from a local scope, and you call the method like this; this.handleClick(), you are telling JavaScript to implement the task of the method IMMEDIATELY it gets there (in your case, immediately it is rendered), which conventionally, doesn't require binding to this.
But when you 'call' (I put it in quotations because the right word should be REFER) a method like this; this.handleClick, you are actually referring to the method (meaning it should be invoked only when the user does something), not invoking it immediately. This either requires binding this.handleClick= this.handleClick.bind(this); or the use of arrow function for your method handleClick = () => {};. It is mostly used for onClick functionalities.
You are not getting that error in your code because you included the parentheses - this.handleIncrement(). If you remove the parentheses and still consoleLog this.state in your handleIncrement, you will definitely get undefined error. But if your handleIncrement is only logging something outside a state, you will not get the error.
If you understand my basic explanation, kindly accept the answer.
Any function called directly from render method will get the container object as this
But when we assign a function to onClick event, we don't want to call that function immediately... so we assign it like this
<button onClick={this.handleIncrement}>
(only the function name without () at the end) ... and this says to call the function when the button is clicked.
But when you click the button the function will not be called from the render method anymore so the this reference will be changed and produce an error.
In your case, you added the () to your this.handleIncrement function invoking it immediately... so it's not causing any problem but it will give you wrong results in almost all cases since it won't get called on click but it will get called with each render.
Since your simple code gets rendered only on button click it's probably correcting the problem. Add a second button and it will give wrong result or the UI will freeze.
The correct way is to remove the () after this.handleIncreament and bind the function inside constructor ... this.handleIncreament = this.handleIncreament.bind(this)
Without bind() method you can use directly arrow function in handleincrement.
check below code
const { Component } = React;
class Counter extends Component {
state = { count: 0 };
handleIncrement=()=> {
const { count } = this.state;
this.setState({ count: count + 1 });
}
render () {
return <label>
<div>Count {this.state.count}</div>
<button onClick={this.handleIncrement}>Increment</button>
</label>;
}
}
ReactDOM.render(<Counter/>, document.querySelector('main'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main/>
I agree with Afzal Hossain.
<button onClick={this.handleIncrement()}>
This line will call handleIncrement function on render. This is not the correct way to add an event.
<button onClick={this.handleIncrement}>
This will be the correct approach to call the function. But since it's a callback, it will have no knowledge of what this is since it's not in the same context.
React Documentation makes it really clear why we should always bind callback functions with this to have the context available in that particular function.
However, if you don't want to bind your function, there are two workarounds mentioned in react documentation
Public Class Fields syntax
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0,
};
handleIncrement = () => {
console.log(this.state);
console.log(this.props);
}
render() {
return (
<div>
<button onClick={this.handleIncrement}>
Increment
</button>
</div>
);
}
}
export default Counter;
ReactDOM.render(<Counter/>, document.querySelector('main'));
Arrow functions
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0,
};
handleIncrement() {
console.log(this.state);
console.log(this.props);
}
render() {
return (
<div>
<button onClick={() => this.handleIncrement()}>
Increment
</button>
</div>
);
}
}
export default Counter;
For more details, refer to this documentation link.
As Afzal Hossain says, you're invoking this.handleIncrement() when the element renders rather than when the button is clicked.
You need to provide the function handle itself to onClick, and bind() it to the correct context when it is constructed, so that this always accesses the instance of Counter within handleIncrement().
Here is a working implementation of the suggestions made in his answer:
const { Component } = React;
class Counter extends Component {
state = { count: 0 };
handleIncrement = this.handleIncrement.bind(this);
handleIncrement () {
const { count } = this.state;
this.setState({ count: count + 1 });
}
render () {
return <label>
<div>Count {this.state.count}</div>
<button onClick={this.handleIncrement}>Increment</button>
</label>;
}
}
ReactDOM.render(<Counter/>, document.querySelector('main'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<main/>

React passing parameter with arrow function in child component

I have these parent and child component, I want to pass click function to select an item in child component. Yet it seems the function in child component become automatically called instead of waiting until the user click the element. To make it clearer here is my parent and child components
export class ParentView extends Component {
state = {
selectedItem: {}
}
handleClick = (item) => {
alert('you click me');
this.setState({selectedItem: item});
}
render() {
let item = { name: 'Item-1' };
return (
<div>
<ChildItem item={item} handleClick={this.handleClick} />
</div>
);
}
}
export class ChildItem extends Component {
render() {
const {item, handleClick} = this.props;
return (
<div>
<a onClick={handleClick(item)} />
</div>
);
}
}
Those are my components using arrow function to pass handleClick to child component, yet alert always being called at first render without being triggered by user. Any suggestion?
You should pass a function itself to onClick, not a result of the passed function invocation.
If you would like to invoke it with param, you have options:
bind it with item with handleClick.bind(this, item). bind creates a new function will have a predefined first parameter - item
pass new arrow function like () => handleClick(item)
An example below:
export class ChildItem extends Component {
render() {
const { item, handleClick } = this.props;
return (
<div>
<a onClick={() => handleClick(item)} />
</div>
)
}
}
In your code you're invoking a function in onClick declaration, so the result of handleClick execution will be passed to onClick, what is most likely not something you wanted to achieve.
<a onClick={handleClick(item)} />
Update:
as #dhilt wrote, there is a drawback of such approach. Since the newly created arrow function and .bind also creates new function every time the render method of ChildItem is invoked, react will threat the resulted react element as a different, comparing to the previous "cached" result of render method, that means that likely it might lead to some performance problems in the future, there is even a rule regarding this problem for eslint, but you shouldn't just follow this rule because of two points.
1) performance problems should be measured. we don't forbid using Array.prototype.forEach in favor of a regular for because for is the same or "faster".
2) definition of click handlers as class properties leads to increasing of the initializing step of the component instance. Re-render is fast and efficient in react, so sometimes the initial rendering is more important.
Just use what's better for you and likely read articles like this https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578
Accepted answer has a performance hit: ChildItem component will be re-rendered even if data hasn’t changed because each render allocates a new function (it is so because of .bind; same with arrow functions). In this particular case it is very easy to avoid such a problem by getting handler and its argument right from the props on new public class field:
export class ChildItem extends Component {
onClick = () => {
this.props.handleClick(this.props.item);
}
render() {
return (
<div>
<a onClick={this.onClick} />
</div>
);
}
}
ParentView remains untouched.
The ES6 way:
Using arrow functions =>
onClick={() => handleClick(item)}
(#havenchyk's answer is the ES5 way).

How to access props within a component?

Is it possible to access myVar value within handleClick? Still struggling to get the basics right :-). Tried passing it within {myVar} but still the same.
var Button = React.createClass({
getInitialState(){
return {counter: 1}
},
handleClick(){
console.log(this.props.myVar); //getting undefined here
},
render(){
return(
<button onClick={this.handleClick} myVar="blah">{this.state.counter}</button>
);
}
});
Props are available everywhere on your Component which means that there is no need for you to pass it as a prop to the button tag. Just a correctly bound function will take care of that for you and since you're using .createClass() your functions are automatically bound to the instance of your component meaning that
the handleClick function already has access to both this.props and this.state.
handleClick: function (event) {
console.log(this.props.myVar); //blah
}
But if you want to pass an extra variable to your function handleClick you need to pass a new function to your onClick handler. Remember to bind this so you have access to your instance as well
handleClick: function (event, myVar) {
console.log(myVar);
}
<button onClick={this.handleClick.bind(this, myVar)} />
A property is bound to the component itself, so this.props will always point to the properties added via the parent, and not each part of markup like angular directives. To bind a variable to the onclick, you can do it like this:
<button onClick={() => this.handleClick("blah")}>{this.state.counter}</button>
Then youre handleclick will fetch it as its first parameter.
You can of course create your own button-component, and then pass in props like you do. Then the button-component will contain this.props.myVar
You can bind the variable and access as a parameter in handleClick function :
var Button = React.createClass({
getInitialState(){
return {counter: 1}
},
handleClick(myVar){
console.log(myVar) //consoles blabh;
},
render(){
let myVar="blabh";
return(
<button onClick={this.handleClick.bind(myVar)} >{this.state.counter} </button>
);
}
});
The thing is that events are not bound to your component by default, so to fix that just bind the the handler to this.
<button onClick={this.handleClick.bind(this)} myVar="blah">{this.state.counter}</button>

Resources