I am trying to refactor the following code from my render view:
<Button href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" 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="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" active={!this.state.singleJourney} onClick={this.handleButtonChange(false)} >Retour</Button> <Button href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" active={this.state.singleJourney} onClick={this.handleButtonChange(true)} >Single Journey</Button> </ButtonGroup> </FormGroup> </Well> </Form> ); } } module.exports = Search;
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" active={!this.state.singleJourney} onClick={this.handleButtonChangeSingle} >Retour</Button> <Button href="#" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" active={this.state.singleJourney} onClick={this.handleButtonChangeRetour}>Single Journey</Button>
Method 2
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>
)
}
Method 3
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
Method 4
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);}
Method 5
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.
Method 6
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>
Method 7
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()
.
Method 8
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.
Method 9
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) }
Method 10
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)}
Method 11
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> } }
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0