Getting uncontrolled input error even though everything is controlled - reactjs

I need your help, I'm trying to use a form from react-strap and for some reason I get this error:
Warning: A component is changing a controlled input of type password to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component
this is my code.
everything is controlled but I still get the error, can anyone spot my mistake?
const LoginForm = () => {
const [formData, setFormData] = useState({
email: " ",
password: " "
});
const onChange = e => {
setFormData({
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
console.log("email password are", formData.email, formData.password);
loginUser(formData.email, formData.password);
};
return (
<Form onSubmit={onSubmit}>
<FormGroup>
<h1>Login</h1>
</FormGroup>
<FormGroup>
<Label for="Email">Email</Label>
<Input
type="email"
name="email"
id="Email"
placeholder="example#mail.com"
onChange={onChange}
value={formData.email}
/>
</FormGroup>
<FormGroup>
<Label for="Password">Password</Label>
<Input
type="password"
name="password"
id="Password"
placeholder="password"
onChange={onChange}
value={formData.password}
/>
</FormGroup>
<Button>Submit</Button>
</Form>
);
};

Ok I found the error, I didn't spread the last answer, so by inputting password I overwritten my email, the proper on change should be this:
const onChange = e => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};

Related

How can i put value in input?

Why can't I put a value in the input? The problem is: I need to put a 'name' in <Form.Item> to apply the rules. Without that, the rules will not be able to work, but if you remove the name="userName" from <Form.Item the value appears at the input.
How can i solve that?
<Form autoComplete="off" layout="vertical" onFinish={handleFinish}>
<Form.Item name="userName" rules={getTextRule('name')}>
<Input value={fields?.firstName} name="firstName" onChange={(e) => {
handleInputChange(e)
}} />
</Form.Item>
</Form.Item>
simple we can code like
const [text,setText] = useState('')
return(
<input type='text' value={text} onChange={e=>setText(e.target.value)}/>
)
If you use the form you can let Ant Form manage the state by removing the value & the onChange props on the <Input />.
Else if you manage the state by yourself make sure to get the value from the e.target.value.
ex:
const [fields, setFields] = useState({})
const handleOnChange = (e) => {
setFields((state) => {
...state,
[e.target.name]: e.target.value,
})
}
return (
<Input
name="firstName"
value={fields.firstName}
onChange={handleOnChange}
/>
)

How to do Jest testing for form validation errors?

I am trying to test a login form validation errors:
<Form className='form' onSubmit={onSubmit} data-testid='form'>
<FormGroup className='form-group'>
<input
type='email'
placeholder='Email Address'
name='email'
value={email}
onChange={onChange}
required
/>
</FormGroup>
<FormGroup className='form-group'>
<input
type='password'
placeholder='Password'
name='password'
value={password}
onChange={onChange}
minLength='6'
/>
</FormGroup>
<input type='submit' value='Login' />
</Form>
but the problem is i am unable to get the error text in the
component = render()
my test is as follows:
it('validate user inputs, and provides error messages', async () => {
const { getByTestId, getByText, getByPlaceholderText } = component
fireEvent.change(screen.queryByPlaceholderText(/Email Address/i), {
target: {value: ""},
});
fireEvent.change(screen.queryByPlaceholderText(/Password/i), {
target: {value: ""},
});
fireEvent.submit(getByTestId("form"));
expect(getByText("Please fill out this field.")).toBeInTheDocument();
})
I am using react testing library with jest
The text displayed on submit is drawn by the browser, not by DOM, so it cannot be inspected with JavaScript.
You may need to add a JavaScript handler for that to sync the actual text to the DOM.
const [errorText, setErrotText] = React.useState("");
const handleInvalid = () => {
setErrorText("Please fill out this field.");
};
const handleValid = () => {
setErrorText("");
};
return (
<input onInvalid={handleInvalid} onValid={handleValid} />
);
Or, you can check the detailed error type by inspecting the ValidityState by accessing element.validity of the input element.
DOM validation is accessible regardless of the testing framework or forms library you use.
For example:
expect(getByPlaceholder("Password").validity.tooShort).toBe(false);

How to add n number of emails in react?

I have created a user registration form which contains of firstname,lastname,email etc.
Here a user can add n number of emails.For this,I have created a button and onClick it should open the input field.
<--Here is the code -->
const [formData, setFormData] = useState({
firstname: "",
lastname: "",
email: "",
})
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
const handleSubmit = async (e) => {
e.preventDefault();
const newStudent = {
firstname,
lastname,
email,
}
try {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify(newStudent);
await axios.post(`http://localhost:5000/student`, body, config);
}
const handleClick = () => {
return <Input type="email" name="email" value={email} onChange={handleChange} />
};
return (
<Form className={classes.root} onSubmit={handleSubmit}>
<div>
<Input
id="standard-multiline-flexible"
label="First Name"
multiline
name="firstname"
value={firstname}
onChange={handleChange}
/>
</div>
<div>
<Input
id="standard-multiline-flexible"
label="Last Name"
name="lastname"
value={lastname}
onChange={handleChange}
multiline
/>
</div>
<div>
<Input
id="standard-multiline-static"
type="email"
label="Email"
name="email"
onChange={handleChange}
value={email}
multiline
/>
<button className="btn btn-primary" onClick={handleClick}>
Add
</button>
<PrimaryButton>Submit</PrimaryButton>
</Form>
)
Can anyone tell me how should I define the handleClick() so that whenever I click the add button,it renders a new Input field?
Another way you could achieve this if you want the user to see their input emails remove them etc is to do the following:
Create an emails state array.
Add an email text field with an add button next to it.
When the user enters an email and presses add, add that email to the array.
You can then map the array to show the added emails as a list with the option to delete the emails if the user decides to.
At the end of handle click
Instead of doing this
return <Input type="email" name="email" value={email} onChange={handleChange} />
Do this ->
setFormData({firstname: "",lastname: "",email: ""});
Edits
You can display the emails via a list after making the API call
You can have a delete button to delete that email from the list.
"There are n ways to implement. Please be more specific"

sweetalert pop up if passwords don't match

I want to make good pop up to my project with sweetalert when the passwords don't match:
const SignUp = ({ signUpStart, userError, resetError }) => {
const [userCredentials, setUserCredentials] = useState({
displayName: "",
email: "",
password: "",
confirmPassword: "",
});
const { displayName, email, password, confirmPassword } = userCredentials;
const handleSubmit = async (event) => {
event.preventDefault();
if (password !== confirmPassword) {
alert(`password don't match`);
return;
}
signUpStart({ displayName, email, password });
};
const handleChange = (event) => {
const { name, value } = event.target;
setUserCredentials({ ...userCredentials, [name]: value });
};
return (
<div className="signup">
<h2 className="title">I do not have a account</h2>
<span>Sign up with your email </span>
<form className="sign-up-form" onSubmit={handleSubmit}>
<Form
type="text"
name="displayName"
value={displayName}
onChange={handleChange}
label="Display Name"
required
></Form>
<Form
type="email"
name="email"
value={email}
onChange={handleChange}
label="Email"
required
></Form>
<Form
type="password"
name="password"
value={password}
onChange={handleChange}
label="Password"
required
></Form>
<Form
type="password"
name="confirmPassword"
value={confirmPassword}
onChange={handleChange}
label="Confirm Password"
required
></Form>
<CustomButton type="submit">SIGN UP</CustomButton>
</form>
{userError && userError.message ? (
<SweetAlert
show={!!userError.message}
type="warning"
title="Something went wrong"
text={userError.message}
onConfirm={() => resetError()}
/>
) : null}
</div>
);
};
EDIT: i add my whole component to the question. i deleted my mapstatetoprops and dispatch.
So i basically want to run once when i hit my submit button.
I want to make the alert in the if statement. But I think I need to refactor little. Can anyone help with this?

React - this.input.value vs handle change

What is better?
I have a form with 10 inputs.
Should I use this.input.value or handle change and store it in state?
handleChange(e) {
this.setState({input: e.target.value});
}
...
<input type="text" value={this.state.input} onChange={this.handleChange} />
or
onSubmit() {
const inputValue = this.input.value;
...
}
...
<input type="text" ref={(input) => {this.input = input;}} />
From the documentation:
When to Use Refs
There are a few good use cases for refs:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
Avoid using refs for anything that can be done declaratively.
Setting up controlled inputs is kind of a pain, but I use this pattern to make it a little easier.
Create one onChange event handler for ALL inputs:
handleInputChange(e){
const target = e.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
Then, for your inputs, be sure to give it a name that matches a key in your state to update.
render() {
const { firstName, lastName, email, acceptTerms } = this.state;
return (
<form>
<input name="firstName" onChange={this.handleInputChange} value={firstName} />
<input name="lastName" onChange={this.handleInputChange} value={lastName} />
<input name="email" onChange={this.handleInputChange} value={email} />
<input type="checkbox" name="acceptTerms" onChange={this.handleInputChange} checked={acceptTerms} />
</form>
)
}

Resources