ReactJS - button onClick gets called during render - javascript

I have a form with type="range". Now I would like to add 3 buttons that change the same value that the form does. For some reason, the buttons onClick event seems to get called repeatedly upon calling the render function.
This is my component:
class Slider extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleButton = this.handleButton.bind(this);
}
handleChange() {
this.props.onSetCountdown(parseInt(this.refs.seconds.value, 10));
}
handleButton(value) {
this.props.onSetCountdown(parseInt(value, 10));
}
render() {
return(
<div>
<form className={styles.ttSlider} ref="form">
<input max="480" min="60" name="slider" onChange={this.handleChange} ref="seconds" type="range" value={this.props.totalSeconds}/>
<button onClick={this.handleButton(60)}>1min</button>
<button onClick={this.handleButton(180)}>3min</button>
<button onClick={this.handleButton(300)}>5min</button>
</form>
</div>
)
}
}
Slider.propTypes = {
totalSeconds: React.PropTypes.number.isRequired,
onSetCountdown: React.PropTypes.func.isRequired
};
And this is from the parent component:
handleSetCountdown(seconds) {
this.setState({
count: seconds
});
}
From the parent component render:
<Slider totalSeconds={count} onSetCountdown={this.handleSetCountdown}/>
This is the error that I get:
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.
To me this looks like the buttons onClick gets called while the component is still rendering. What am I doing wrong?

It's because instead of passing the function to the event onClick, you're calling the function directly.
Try doing it this way:
<button onClick={() => { this.handleButton(60)}}>1min</button>
<button onClick={() => { this.handleButton(180)}}>3min</button>
<button onClick={() => { this.handleButton(300)}}>5min</button>
Found the answer here: React onClick function fires on render
Hope it helps!

If you dont want to use anon functions for any reason, the second method is to use bind directly at render function. Then you can delete lines at your constructor :)
<button onClick={this.handleButton.bind(this, 60)}>1min</button>

Related

How can I setState() another input value with a button in the same component in React?

How can I setState() another input value with a button in the same component in React?
I'm using the onClick event handler on the button.
I want to make the handleClickfunction which I gave it to the button, to target the value of the input
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: input.value,
});
};
render() {
return (
<div>
<input type="text"/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
Your question is not clear, I believe you are asking how to set the value of an input field when you press a button in react.
If that is correct, then you have done most of the work already, all you need to do now is add an <input> tag.
Like this:
<input type="text" value={ this.state.searchInput } />
If I have misunderstood your question then please clarify.
It may be worth reading about how State and Lifecycle work in React Here
Whenever the setState() function is triggered, React automatically runs the render() function in any components where state has changed, rerendering that component with the new state values.
Edit
After clarification I now understand exactly what you want.
You require the use of a ref, like this:
class Search extends Component {
state = {
searchInput: "",
};
handleClick = () => {
this.setState({
searchInput: this.inputText,
});
};
render() {
return (
<div>
<input type="text" ref={(x) => this.inputText = x}/>
<button onClick={this.handleClick}>Enter</button>
</div>
);
}
}
instead of using a button to update the state try this:
<input type="text" onChange={(e) => this.setState({searchInput: e.target.value }) />

using a simple component (dumb) to create a list of buttons and use parent method

I'm trying to create a simple dashboard. I'm just exploring some new ideas I have in react and it's been so long I'm running into a strange problem I can't seem to understand.
I have a very simple class:
export default class Dashboard extends React.Component {
constructor(){
super();
}
HandleClick = (e) => {
if (e.name === "createEvent") {
console.log('event clicked');
}
console.log(e.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={this.HandleClick}/>
</div>
)
}
}
and then I have a simple function outside of the class that creates a button list:
function ButtonList(props) {
return (
<button name="createEvent" onClick={props.HandleClick}>Create Event</button>
)
}
the idea behind this was instead of having so much stuff inside one superclass I wanted to separate simple functionality, like a button or command list if you will, that opon clicking would eventually change the state of the navbar.
I'm not sure how I would return that values of the button, or aside from that pass a parameter into the button from a child prop.
For example instead of doing HandleClick = (e) => and actually look for a parameter, how would I pass that in the child function where it gets used (if there were many more buttons)?
This is what you should be doing instead:
On your parent component, you can use arrow functions to pass the parameters within handleClick. This will allow you to listen to the events on your child ButtonList component with the parameters passed onto the method.
In addition, if you want to access to name attribute of your button, you should be calling event.target.name, as name is part of the target property of the Event interface.
export default class Dashboard extends React.Component {
constructor(){
super();
}
handleClick = (e) => {
if (e.target.name === "createEvent") {
console.log('event clicked');
}
console.log(e.target.name);
}
render() {
return(
<div className="row">
<ButtonList onClick={(e) => this.handleClick(e)} />
</div>
)
}
}
And on your ButtonList functional component, you should pass the onClick event to the onClick props which was defined as part of the ButtonList component.
function ButtonList(props) {
const onClick = (e) => {
props.onClick(e);
};
return (
<button name="createEvent" onClick={(e) => onClick(e)}>Create Event</button>
)
}
I have created a demo over here.

Modal element is not hiding [React & React - Bootstrap]

I've created modal with React Bootstrap. Despite the fact, that I'm using onHide function, nothing happens. Here's my modal:
<React.Fragment>
<Modal
{...this.props}
bsSize="large"
aria-labelledby="contained-modal-title-lg"
show={this.state.showModal}
onHide={this.handleHide}
>
<Modal.Body>
...
</Modal.Body>
<Modal.Footer>
<Button id = "closeModal" variant="danger" onClick=
{this.handleHide.bind(this)}>
Save and close
</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
I'm passing "show" from other component, when click occurs on some element. onClick on that element is specified to: "showModal: true". Then I'm passing showModal to other component that is rendering different elements according to option choosed:
{this.state.addingState && (
<MyForm
{...this.state.item}
show={this.state.showModal}
...
/>
)}
At last in MyForm component I have function that passes props to component with modal:
createModalComponent {
...
let modalComponentProps= {
...
show: this.props.show,
}
So, this is the way "show" is going. In my file with modal component I have function for handling hide:
handleHide() {
this.setState({ showModal: false });
}
Now in this component showModal is initialize in state like so:
constructor(props) {
super(props);
this.state = {
showModal: this.props.show
};
this.handleHide = this.handleHide.bind(this);
}
I've tried many things. Other state variables, without initializing showModal in state and many more. When clicking on the button or beyond the modal, modal is still visible and not hiding. I will be very grateful for your help and/or suggestions how to fix this.
So, the way showModal is going: parent component (where this.state.addingState is happening) -> MyForm component (where let modalComponentProps= { show: this.props.show, ... happens) -> actual modal component
Code on CodePen
you have 2 possibilities: you can add the method to your parent and pass the method + the result of show like this (use same name of props and method for best practice, so you will be not confuse):
Parent
<Modal
{...this.props}
bsSize="large"
aria-labelledby="contained-modal-title-lg"
show={this.state.showModal}
handleHide={this.handleHide}
>
Child
<MyForm
show={this.props.showModal}
handleHide={this.props.handleHide}
/>
To use the modal from parent, you can call it like this in child: this.props.handleHide(true).
Or you let the child manage the state by itself, so you would place the method and state in child, not in parent (depending on the architecture).
It is not recommended to add the props in child state.
Also, you could use es6 function to avoid binding like this:
handleHide = () => this.setState({ showModal: false });
Look on the shouldComponentUpdate method
shouldComponentUpdate(nextProps) {
return !isEqual(this.props, nextProps);
}
You are checking only props but you are changing the state of the component. Fix the method or remove it and it will be working

Pass prop value as a parameter in an arrow function in an event handler onClick() in React

I'm trying to pass a value of prop in a function which is invoked on onClick(), but I'm getting the following error when I try to console.log() that value inside the function.
Error:
**Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property nativeEvent on a
released/nullified synthetic event. This is set to null. If you must
keep the original synthetic event around, use event.persist().
class Calculator extends React.Component {
constructor(props) {
super(props);
this.evaluateInfixString = this.evaluateInfixString.bind(this);
this.appendInfixString = this.appendInfixString.bind(this);
this.state = {
buttons: ["+", "-", "*", "/", 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, "clear", "="],
infixString: null
};
}
evaluateInfixString() {
console.log("perform operation");
}
appendInfixString(buttonPressed) {
console.log(buttonPressed);
}
render() {
return (
<div id={"calculator"}>
<Display value={this.state.infixString} />
<Buttons
buttons={this.state.buttons}
appendInfixString={this.appendInfixString}
/>
</div>
);
}
}
class Display extends React.Component {
render() {
return <div id={"display"}>{this.props.value}</div>;
}
}
class Buttons extends React.Component {
render() {
return (
<div id={"buttons"}>
{this.props.buttons.map(button => {
return <button className={"button"} onClick={(button) => this.props.appendInfixString(button)}>{button}</button>;
})}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Calculator />, rootElement);
Change:
return <button className={"button"} onClick={(button) => this.props.appendInfixString(button)}>{button}</button>;
To:
return <button className={"button"} onClick={() => this.props.appendInfixString(button)}>{button}</button>;
Here's a working codepen. If you open the console, you'll see that the number or character is logged to the console for you.
Another way to accomplish what you'd like to do would be something like what I've done in this codepen. You'll be passing the event back to the parent and then you can access the value with e.target.value like I've shown in your parent component. In that case, you're child component would have a click handler like this:
<button type="button" className={"button"} value={button} onClick={this.props.appendInfixString}>{button}</button>
The button value will be passed back with the event in the parent click event handler, which you can access there.
What is passed to onClick is click event, so in your code below
<button className={"button"} onClick={(button) =>this.props.appendInfixString(button)}>{button}</button>
what you pass to the appendInfixString is not the button string that the user clicks, but rather the click event. If you need to pass the button string being clicked, try
<button className={"button"} onClick={() => this.props.appendInfixString(button)}>{button}</button>

React Bootstrap Dropdown Button OnSelect

I am passing the option values into a series of Dropdown buttons, each of which is in a child component from a data array.
When an option is chosen in one of the buttons I am updating the state in the parent component with the result of onSelect. This is all working fine...
//parent component
sourceSelected = (event) => {
this.setState({
sourceSelected: event
});
...
<ButtonToolbar>
{MEDIUM.map((medium) =>
<Medium key={medium.medium_name} medium={medium} onSelectedValue{this.sourceSelected } />
)};
</ButtonToolbar>
//child component
<DropdownButton title={props.medium.medium_name} id="source-dropdown" onSelect={props.onSelectedValue}>
{props.medium.source.map((option, index) =>
<MenuItem key={index} eventKey={option}> {option} </MenuItem>)}
</DropdownButton>
However, I would also like to store in the state (mediumSelected=???) the name of the button from which the option was selected.
Is there anyway to get OnSelect to pass this back or should I do something else?
OK, I answered this using... https://reactjs.org/docs/handling-events.html passing arguments to event handlers.
The code is:
//parent component
sourceSelected = ( medium_name, event) => {
this.setState({
sourceSelected: event,
mediumSelected: medium_name
});
}
...
<div className='test'>
<ButtonToolbar>
{MEDIUM.map((medium) =>
<Medium key={medium.medium_name} medium={medium} onSelectedValue={this.sourceSelected.bind(this, medium.medium_name) } />
)};
</ButtonToolbar>
You can take advantage of Javascript events and this. Basically, pass the event to the function that will be using the button name, like this
<button name="btn" onClick={e => this.buttonName(e.target.name)}>
You will also need to bind this in your constructor()
Example code:
constructor(props) {
super(props);
// Bind this so you can use it below
this.buttonName = this.buttonName.bind(this);
}
buttonName(e) {
console.log(e);
}
render() {
return (
<div>
// Pass the name of the button to the function
<button name="btn" onClick={e => this.buttonName(e.target.name)}>
Button
</button>
</div>
);
}
I also threw a quick example on https://codesandbox.io/s/lrwqr303vz. Don't mind the file names.

Categories

Resources