I have a react application, where I'm using a handler the set the state, for some input forms
<div class="form-group has-danger">
<label class="form-control-label" for="inputDanger1">Username</label>
<input type="text" name="username" value={this.state.username} onChange={this.onChange} className={this.state.isUsernameCorrect ? "form-control is-valid" :"form-control is-invalid"} id="inputInvalid" />
{this.state.isUsernameCorrect ? <div class="valid-feedback">Username is acceptable!</div> :
<div class="invalid-feedback">Please enter a valid username</div> }
</div>
<div class="form-group has-danger">
<label class="form-control-label" for="inputDanger1">Password</label>
<input type="password" name="password" value={this.state.password} onChange={this.onChange} className={this.state.isUsernameCorrect ? "form-control is-valid" :"form-control is-invalid"} id="inputInvalid" />
{this.state.isPasswordCorrect ? <div class="valid-feedback">password is acceptable!</div> :
<div class="invalid-feedback">Please enter a valid password</div> }
</div>
with a respective handler
onChange = (e) =>{
const name = e.target.name;
const value = e.target.value;
this.setState(
{[name] : value
},
() => this.usernameValidator(this.state.username),
() => this.passwordValidator(this.state.password)
);
}
I want to pass two separate functions, which validates the username and password with a regex pattern. Furthermore, it controls the state, of the display messages, if the password/username is correct or not.
However I want to execute both functions (and more maybe, for adding more form data), but setSatet only accepts one callback?
setState callback accepts only one function, but inside you can pass as many functions as you like.
this.setState({ [name] : value },
() => {
this.usernameValidator(this.state.username);
this.passwordValidator(this.state.password);
}
);
Related
Below is a authentication component that handles both registering and login of a user into an application. The approach used was to conditionally render a div based on whether a user had signed up in the application before or they are a new user. The div doesn't change based on the initial state and based on the conditions set. In this case, the isSignUp state is false therefore the user hasn't signed up before hence all the fields are supposed to be available for data input by the user but the fields have been ommited. Below is the code
const {isSignUp, setisSignUp} = useState(false);
<form onSubmit = {handleSubmit} className = 'form'>
{isSignUp && (
<div className='auth-form-field'>
<input
name="Full Name"
type="text"
placeholder="Full Name"
className = "form-input"
onChange={handleChange}
required
/>
</div>
)}
<div className='auth-form-field'>
<input
name="Email"
type="text"
placeholder="Email"
className = "form-input"
onChange={handleChange}
required
/>
</div>
{ isSignUp && (
<div className='auth-form-field'>
<input
name="User Name"
type="text"
placeholder="User Name"
className = "form-input"
onChange={handleChange}
required
/>
</div>)
}
<div className = 'auth-form-field'>
<input
name="Password"
type="password"
placeholder="Password"
className = "form-input"
onChange={handleChange}
required
/>
</div>
{isSignUp && (
<div className = 'auth-form-field'>
<input
name="Confirm Password"
type="password"
placeholder="Confirm Password"
className = "form-input"
onChange={handleChange}
required
/>
</div>)
}
</form>
Your syntax of useState is incorrect here
Correct syntax:
const [isSignUp, setisSignUp] = useState(false);
The Correct syntax to use useState is with square brackets, like this
const [isSignup, setIsSignUp] = useState(false);
Here is my class component
handleChange =(e) => {
this.setState({
[e.target.id]: e.target.value
})
}
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={this.handleChange}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={this.handleChange}/>
</div>
As you can see we can track all the input with one function component.
But how to get the same result as that inside the functional component?
Now I am setting state for each element.
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={e =>setEmail(e.target.value)}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={e => setPassword(e.target.value)}/>
</div>
The update is similar. Given some state, data for example, then a handler may look like:
const handleChange = (e) => {
const { id, value } = e.target;
setData((data) => ({
...data,
[id]: value
}));
};
Use a functional state update to spread in the existing state and use the input's id and value from the onChange event to update that section of state.
Full code example
function App() {
const [data, setData] = useState({});
const handleChange = (e) => {
const { id, value } = e.target;
setData((data) => ({
...data,
[id]: value
}));
};
return (
<div className="App">
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={handleChange} />
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={handleChange} />
</div>
<div>Email: {data.email}</div>
<div>Password: {data.password}</div>
</div>
);
}
declare one state containing both email and pasword for your functional component as below :
let [loginInfo, setLoginInfo] = useState({email:'',password:''})
handleChange =(e) => {
setLoginInfo({
...loginInfo,
[e.target.id]: e.target.value
})
}
you form
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" onChange={handleChange}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" onChange={handleChange}/>
</div>
You can create object of form input and assign it to a state like below
const [formInput, setFormInput] = useState({
email: "",
password: ""
});
And set it like this
const handleChange = (e) => {
setFormInput((prevState) => ({
...prevState,
[e.target.id]: e.target.value
}));
};
See this codesandbox code
const [input, setInput] = useState({email:"", password:""})
<div className="input-field">
<label htmlFor="email">Email</label>
<input type="email" id="email" value={input.email}
onChange={e => setInput({...input,[e.target.id]: e.target.value}}/>
</div>
<div className="input-field">
<label htmlFor="password">Password</label>
<input type="password" id="password" value={input.password}
onChange={e => setInput({...input,[e.target.id]: e.target.value}}/>
</div>
You can try this code.
In this way you can handle both inputs using same state without handler
I have a drop-down where I have several option, what I want to do is which ever option is selected I want to create that much of input fields
On change of select I am doing this
const Select_change = (e) => {
let val = parseInt(e.target.value);
console.log(val);
switch (val) {
case 2:
setinputHolder((inputHolder) => [...inputHolder, 2]);
case 3:
setinputHolder((inputHolder) => [...inputHolder, 3]);
case 4:
setinputHolder((inputHolder) => [...inputHolder, 4]);
console.log(inputHolder);
default:
setinputHolder((inputHolder) => [...inputHolder, val]);
}
};
<select
onChange={Select_change}
name="Select_op"
className="form-control"
ref={register({ required: 'Please select IOT hub' })}>
<option value={1}>1</option>
<option value={2}>2</option>
<option value={3}>3</option>
<option value={4}>4</option>
</select>
I have created one state initial which is set to be 1 as I want to show one input field,after this what I am doing is
const [inputHolder, setinputHolder] = useState([1]);
I am looping the input field on this basis
{inputHolder.map((li, index) => (
<div className="col-12 col-sm-12 col-md-8 col-lg-4 col-xl-4">
<div className="form-group input_form_label">
<input
type="text"
name="device_name"
id="device_name"
className="form-control"
placeholder="Device Name"
/>
<label className="common_label_form" htmlFor="device_name">
Device Name
</label>
</div>
</div>
))}
But it is not working as expected, what If I have unknown nos in my drop-down let say 100, so I can not use switch to 100, I want a dynamic solution which will work fine.
Here is my working code sandbox for better understanding
Do below changes in your code
const Select_change = (e) => {
let val = parseInt(e.target.value);
setinputHolder(val);
};
and while making input based on selected value
{[...Array(inputHolder).keys()].map(...)
so your logic will look like below code
<div className="row">
{[...Array(inputHolder).keys()].map((li, index) => (
<div className="col-12 col-sm-12 col-md-8 col-lg-4 col-xl-4">
<div className="form-group input_form_label">
<input
type="text"
name="device_name"
id="device_name"
className="form-control"
placeholder="Device Name"
/>
<label className="common_label_form" htmlFor="device_name">
Device Name
</label>
</div>
</div>
))}
</div>
https://codesandbox.io/s/boring-dew-k1chw?file=/src/App.js
I appreciate anyone's response and suggestions. I have a react component that loads a form to edit a product. When the component is mounted, it will receive props from another function and I use these values within the prop as default value of the input fields in the form. I also have an input element that accepts uploading a file. When the file is selected, the component seems to be refreshing and setting other input fields to their default values. Here is my component:
<form onSubmit={handleSubmit}>
<div className="form-row">
<div className="col-md-4 mt-md-2">
<input
type="text"
className="form-control"
name="name"
placeholder="Name"
defaultValue={product!.name}
ref={register({
required: true,
pattern: /^[a-zA-Z ]*$/,
})}
/>
</div>
<div className="col-md-4 mt-2 mt-md-2">
<input
type="text"
className="form-control"
name="qty"
placeholder="Quantity"
defaultValue={product!.qty}
ref={register({ required: true, pattern: /^[0-9]+$/ })}
/>
</div>
</div>
<div className="form-row">
<div className="col-md-4 mt-2 mt-md-2">
<div className="input-group">
<div className="custom-file">
<input
type="file"
className="custom-file-input"
name="posImg"
id="img"
title="Add image to product if it is a menu item"
onChange={handleImg}
/>
<label
className="custom-file-label"
htmlFor="img"
aria-describedby="inputGroupFileAddon02"
>
{fileLabelState}
</label>
</div>
</div>
</div>
</div>
<div className="d-flex justify-content-end mt-2">
<button
type="submit"
className="btn zaad justify-content-end"
title="Submit product edit"
>
Update
</button>
</div>
</form>
The issue is when I choose the file and click on select, all the other input fields are reset to their default values. Here is my handleImg function:
const handleImg = (e: any) => {
e.preventDefault();
const file = e.target.files[0];
setFileLabelState(e.target.files[0].name);
setFile(file);
};
I have tried to work with the e.preventDefault(), but I have had no chance so far. Any suggestions on how I can go about this?
Thank you again.
EDIT:
I fixed the form handler and now here is my handleSubmit function:
const handleSubmit = (event: any) => {
event.preventDefault();
event.stopPropagation();
let editedProd = new FormData();
let isMenu: string = event.menuItemOption === "yes" ? "true" : "false";
editedProd.append("name", event.name);
editedProd.append("qty", event.qty);
editedProd.append("img", file);};
const handleSubmit = event => {
event.preventDefault();
let editedProd = new FormData();
let isMenu: string = event.menuItemOption === "yes" ? "true" : "false";
editedProd.append("name", event.name);
editedProd.append("qty", event.qty);
editedProd.append("img", file);};
}
try this answer note here i change (event: any) to event. this is working code from my project
I am getting this error, I am new in react. I have seen other posts but still could not resolve the issue. help will be appreciated. thanks
Warning: A component is changing an uncontrolled input of type email to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info
in input (created by Edit)
in div (created by Edit)
in div (created by Edit)
in form (created by Edit)
in div (created by Edit)
in Edit
import React from 'react'
import ReactDom from 'react-dom'
export default class Edit extends React.Component{
constructor(){
super();
this.state={
name: '',
email: '',
password: '',
};
}
componentWillMount(){
let id=this.props.id;
axios.get('/api/users/'+id).then(response => {
var user= response.data;
this.setState({
name: user.name,
email: user.email,
})
}).catch(error => {
console.log(error)
})
}
handleNameChange(e){
this.setState({
name: e.target.value
})
}
handleEmailChange(e){
this.setState({
email: e.target.value
})
}
handlePasswordChange(e){
this.setState({
password: e.target.value
})
}
handleSubmit(e){
e.preventDefault();
console.log(this.state)
axios.post('/api/users',this.state).then(response => {
console.log(response)
}).then(error => {
console.log(error)
})
}
render(){
return(
<div>
<h2> Add New User </h2>
<form className="form-horizontal" onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Name:</label>
<div className="col-sm-10">
<input type="text" className="form-control" id="name" placeholder="Enter name" value={this.state.name}
onChange={ this.handleNameChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Email:</label>
<div className="col-sm-10">
<input type="email" className="form-control" id="email" placeholder="Enter email" value={this.state.email}
onChange={ this.handleEmailChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Password:</label>
<div className="col-sm-10">
<input type="password" className="form-control" id="password" placeholder="Enter password" value={this.state.password}
onChange={ this.handlePasswordChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<button type="submit" className="btn btn-default">Update</button>
</div>
</form>
</div>
)
}
}
if (document.getElementById('edit')) {
var id=$("#edit").data("id");
ReactDom.render(<Edit id={id}/>, document.getElementById('edit'))
}
You are using value attribute to set default value while rendering, it makes your input controlled input, then when updating your states you are trying to change your input values again which will make it uncontrolled one. basically for your code to succeed you need to use defaultValue attribute in your rendered code instead of value attr. because when you are setting value to input from html it can't be later changed.
do this instead for your input elements:
<input type="email" className="form-control" id="email" placeholder="Enter email" defaultValue={this.state.email} />
here is a source from react docs on issue:
https://reactjs.org/docs/uncontrolled-components.html
The error is occuring because when you're doing setState inside componentDidMount, the value returned seems to be undefined.
if(user.name!==undefined && user.email!==undefined){
this.setState({
name: user.name,
email: undefined,
})
}
This will prevent the values in the state from becoming undefined and you won't have that error anymore.