This question already has answers here:
How to disable button in React.js
(8 answers)
Closed 3 years ago.
I am using trying to disable a button in react based on couple states. Down below is a breakdown of my code
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
disabled: true
}
this.handleChange = this.handleChange.bind(this);
this.handlePass = this.handlePass.bind(this);
}
pretty self explanatory constructor. The disabled will be changed as state changes. My render method looks something like this
render() {
if(this.state.email && this.state.pass) {
this.setState({ disabled: false })
}
return (
<div className='container'>
<div className='top'></div>
<div className='card'>
<MuiThemeProvider>
<Card >
<div className='wrapper'>
<TextField
hintText="Email"
value={this.state.email} onChange={this.handleChange}
/><br/>
<TextField
hintText="Password"
type="password"
/><br/>
<div className='login-btn'>
<RaisedButton label="Login" primary={true}
disabled={this.state.disabled} />
</div>
</div>
</Card>
</MuiThemeProvider>
</div>
</div>
)
}
As you can see I have 2 text fields and I am handeling the data changes with the following method
handleChange(e) {
this.setState({email: e.target.value});
}
handlePass(e) {
this.setState({pass: e.target.value});
}
Now my button is initially disabled and everytime a state is changed and component re-renders I want to check for state changes and enable button accordingly. So I was thinking of using the life cycle method like so
componentWillMount() {
if(this.state.pass && this.state.disabled) {
this.setState({disabled: false})
}
}
However, this doesn't work. When both email and password field is not empty the button stays disabled. I am not sure what am I doing wrong.
Please, do not set states inside render() function. That might cause infinite loops to occur.
Refer: https://github.com/facebook/react/issues/5591
Instead of setting states inside render() function, you can set the disabled state inside the handleChange() and handlePass() function.
If more detail required, please do mention.
You should be setting the disabled state inside your handleChange and handlePass functions.
componentWillMount() only runs right before the component is rendered, but never again.
Just made a demo , is that you need, check the code in the demo below
demo
Change below code :
class App extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
pass: '',
invalidData: true
}
this.onEmailChange = this.onEmailChange.bind(this);
this.onPasswordChange = this.onPasswordChange.bind(this);
}
// componentWillUpdate is to be deprecated
//componentWillUpdate(nextProps, nextState) {
// nextState.invalidData = !(nextState.email && nextState.pass);
//}
onEmailChange(event) {
this.setState({ email: event.target.value });
}
onPasswordChange(event) {
this.setState({ pass: event.target.value });
}
render() {
return (
<form>
<input value={this.state.email} onChange={this.onEmailChange} placeholder="Email" />
<input value={this.state.password} onChange={this.onPasswordChange} placeholder="Password" />
// from this <button disabled={this.state.invalidData}>Submit</button>
//to
<button disabled={!(this.state.email && this.state.password)}>Submit</button>
</form>
);
}
}
**updated **
disable submit button in <button disabled={!(this.state.email && this.state.password)}>Submit</button> itself.
Related
I have a simple React component that I'm working on creating right now. Basically, the user can input an ID and when they submit, it will display some information that is in a container. The code looks like so
export default class IDContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
Id: '',
isSubmitted: false
};
}
handleSubmit = (event) => {
this.setState({
isSubmitted: true
});
};
handleChange = (event) => {
this.setState({
Id: event.target.value
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<Input type={'text'} placeholder={"Enter Id"} value={this.state.Id} onChange={this.handleChange} />
<Button type={'submit'} > Lookup </Button>
</div>
<div>
{this.state.isSubmitted && <DetailsContainer Id={this.state.Id} />}
</div>
</form>
);
}
}
The details container has already been created and just returns some details about the Id that has been passed in. I can show the details of the first Id that I pass in just fine. However, when I enter in another Id and submit the form, the DetailsContainer is not re-rendering and is still showing the details for the older Id. I tried moving it around and adding some logic (I even put the DetailsContainer in my state to see if I can manipulate it that way), but that doesn't seem to be working. I see that there is a shouldComponentUpdate() method, and that seems to be what I need to use, but the guides I saw all place it inside of the DetailsContainer. Anyway for me to have it in IDContainer, or is there an easier way to re-render the DetailsContainer?
I think part of the issue here is that once isSubmitted is set, every change you make to the input will be applied to this.state.Id and passed into DetailsContainer.
I think you'd be better off having one variable for tracking the input state, and variable one for tracking the Id you want to pass into DetailsContainer.
state = { Id: null, inputId: '' };
handleSubmit = (event) => {
this.setState({
Id: this.state.inputId
});
};
handleChange = (event) => {
this.setState({
inputId: event.target.value
});
};
render() {
return (
...
<Input ... value={this.state.inputId} />
...
{this.state.Id !== null ? <DetailsContainer Id={this.state.Id} /> : null}
);
}
I currently have an input field set up so the inputs value will be console logged when a button is pressed, I am trying to render a component instead of this console.log.
I can get it to sorta work but it re-renders every time I type a single character because I'm not sure how to check for the click within the render method, I read through some of the docs but couldn't figure out how do to it. How could I go about achieving this?
here is the code
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
console.log(this.state.input)
}
render() {
//Not sure how to check for this
if (this.state.input) {
return <Fetch username={this.state.input} />
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
let me know if you need anymore information. Thanks!
Edit: By the way, I want to be able to submit the form many times, sorry, I should have been more descriptive of my problem. I want the Form component to stay rendered and I want the Fetch component to get rendered on clicked
A solution depends on your needs and there might be a lot of them. The simplest way, just to demonstrate:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false, // adding the flag
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true }); // set the flag on submit
console.log(this.state.input)
}
render() {
//Not sure how to check for this
if (this.state.submitted) { // show the component by the flag
return <Fetch username={this.state.input} />
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
Obviously, in real application it's too simple logic. You have to define first, what are you trying to reach.
UPDATE:
If you want to keep the input field and the component simultaneously, you can do it like this:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false, // adding the flag
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true }); // set the flag on submit
console.log(this.state.input)
}
render() {
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
{this.state.submitted && (
<Fetch username={this.state.input} />
)}
</form>
);
}
}
Actually what you are doing is every time the user type a character, you are displaying Fetch component with the input's value passed in props.
With this and with the handleChange method
if (this.state.input) {
return <Fetch username={this.state.input} />
}
The condition here return true if input is different from "". So when a character is type it becomes true and displays it.
In your case what you want to do is to display the Fetch component when you are clicking on the submit button.
What you can do is create a state to manage it.
For example you could have this in your state :
this.state = {
input: '',
fetchIsVisibile: false,
};
Then in your handleSubmit just do this:
handleSubmit(e) {
this.setState({ fetchIsVisible: true }),;
e.preventDefault();
}
And the condition for display your fetch component would be using this new state instead of input state
if (this.state.fetchIsVisible) {
return <Fetch username={this.state.input} />
}
You can add state whether the form is submitted in the state and to check for it when rendering the component
if you want to check whether the form is submitted and the input is not empty you can use (this.state.submitted && this.state.input) instead
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
input: '',
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value});
}
handleSubmit(e) {
this.setState({
submitted: true
});
}
render() {
//Not sure how to check for this
if (this.state.submitted) {
return <div>
<div>FETCH COMPONENT</div>
<div>input: {this.state.input}</div>
</div>
}
return(
<form>
<label>
Name:
<input type="text" value={this.state.input} onChange={this.handleChange} />
</label>
<div>input: {this.state.input}</div>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
</form>
);
}
}
ReactDOM.render(<Form />, document.getElementById('app'))
<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="app"></div>
I have 2 React parent/child components. The Child Component has a button that adds +1 to the previous state of the Parent Component, and a Form that triggers a handleChange function for the onChange event.
The Problem
From the Form input, I want to trigger a function that sets the State to the previous State, + the input in the Form.
For example, if I write 50 in input and hit submit I want the new state be 100
Here is a codesandbox: https://codesandbox.io/s/30mz2vvyo1
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 50
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState((prevState) => {
return { value: prevState.value + 1 }
});
}
handleSubmit(event) {
event.preventDefault();
}
render() {
return (
<div>
<Child value={this.state.value} handleChange={this.handleChange} handleSubmit={this.handleSubmit} />
</div>
)
}
}
class Child extends React.Component {
render() {
return (
<div>
<button onClick={this.props.handleChange}>Count + 1</button>
<div>{this.props.value}</div>
<form onSubmit={this.props.handleSubmit}>
<label>
Name:
<input type="text" onChange={this.props.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
The problem you are facing can be mitigated by;
You need to have two different variables in state. value, can hold your value. You also need to hold the current value of the input, let's call it inputNumber.
You need to provide an onClick function to your button. In said function, set your state in the following fashion;
Code:
this.setState({
value: this.state.value + this.state.inputNumber,
})
After doing these things, it should work as expected.
I have updated your codesandbox, you can take a look at it here.
I have small class in react, i want to display the result on the screen after i click on the button, but before the display happens, the page reload.
how do i do it?
what am I missing?
import React, {Component} from 'react';
class InputFieldWithButton extends Component{
constructor(props){
super();
this.state = {
message: ''
};
}
handleChange(e){
this.setState({
message: e.target.value
});
}
doSomething(e){
return(
<h1>{this.state.message}</h1>
)
}
render(){
return (
<div>
<form >
<input type="text" placeholder="enter some text!" value=
{this.state.message}
onChange={this.handleChange.bind(this)}/>
<button onClick={this.doSomething.bind(this)}>Click me</button>
</form>
</div>
)
}
}
export default InputFieldWithButton;
Your button is inside a form and triggering a submit.
You can use the preventDefault() method to stop it from doing so:
doSomething(e) {
e.preventDefault();
return (
<h1>{this.state.message}</h1>
)
}
By the way, your return statement of this click handler makes no sense at the moment.
Edit
As a followup to your comment:
Can you explain me what is my mistake in the return?
Not really a mistake, but it is useless in this context as your are not doing anything with the returned object.
Where and how do you expect to use the <h1>{this.state.message}</h1> that you are returning?
If you intend to show / hide the input message in your screen you could do it with conditional rendering.
Just store a bool like showMessage in your state and render the message only if it's set to true.
Here is a small example:
class InputFieldWithButton extends React.Component {
constructor(props) {
super(props);
this.state = {
message: '',
showMessage: false
};
}
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
toggleMessage = (e) => {
e.preventDefault();
this.setState({ showMessage: !this.state.showMessage })
}
render() {
const { showMessage, message } = this.state;
return (
<div>
<form >
<input
type="text"
placeholder="enter some text!"
value={message}
onChange={this.handleChange}
/>
<button onClick={this.toggleMessage}>Toggle Show Message</button>
{showMessage && <div>{message}</div>}
</form>
</div>
)
}
}
ReactDOM.render(<InputFieldWithButton />, 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>
By the way, it is considered as bad practice to bind the functions inside the render method, because you are creating a new instance of a function on each render call. instead do it inside the constructor which will run only once:
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
Or you can use arrow functions which will reference this in a lexical context:
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
This is what i've used in my example.
you're not specifying the buttons'type
<button type="button">
Set the type attribute on the button to be button. The default is submit since it is wrapped in a form. So your new button html should look like this:
<button type="button" onClick={this.doSomething.bind(this)}>Click me</button>
I am trying to reset a input field on state update. So when my state updates through a function my view would change as well. Below is my code:
constructor(props){
super(props)
this.state = { song: '',
videos: '' }
this.handleSongInput = this.handleSongInput.bind(this)
}
in my render function I do something like this
render () {
return (
<div>
<TextField
floatingLabelText="Search Songs"
value={this.state.value}
onChange={this.handleSongInput}
/>
<br />
<RaisedButton label="Search" onClick={this.searchSong} />
</div>
)
}
The handle function for the Input field is below. It is simply setting the state.
handleSongInput = (e) => {
this.setState({ song: e.target.value})
}
Now on button click I have the following function which resets the initial
searchSong = () => {
...
this.setState({song:''})
}
Now if I do a console.log I can see that the state has changed. But in my view I can still see that the text field is populated with previous text.
How can I set the value of textfield with current state
I believe you have a variable name issue:
value={this.state.value}
should read:
value={this.state.song}