I have this JS code that shows 2 input boxes that asks for a password: 1) Password 2) Confirm Password. However, the clickShowPassword() is only connected to Password.
[Output] [1]: https://i.stack.imgur.com/IZoa3.png
Here's my whole js file that is connected to an react application.
import React from "react";
function ShowHidePassword(){
const [values, setValues] = React.useState({
password: "",
passwordConf: "",
showPassword: true,
});
const clickShowPassword = (event) => {
setValues({ ...values, showPassword: !values.showPassword });
event.preventDefault();
};
const passwordChange = (prop) => (event) => { setValues({ ...values, [prop]: event.target.value }); };
const mouseDownPassword = (event) => { event.preventDefault(); };
return (
<div>
<input
type={values.showPassword ? "text" : "password"}
onChange={passwordChange("password")}
value={values.password} id="signup-password"
placeholder="PASSWORD"
/>
<input
type={values.showPassword ? "text" : "passwordConf"}
onChange={passwordChange("passwordConf")}
value={values.passwordConf} id="signup-password-confirm"
placeholder="CONFIRM PASSWORD"
/>
<br/>
<button className="hide-password2" onClick={clickShowPassword} onMouseDown={mouseDownPassword}>
{values.showPassword===false? <i className="bi bi-eye-slash"></i> :<i className="bi bi-eye"></i> } Show Password
</button>
</div>
);
};
export default ShowHidePassword;
In your second input you used passwordConf as an input type, I think this happened because u copied the first input and batch-replaced all "password" words with "passwordConf", happens to the best of us :)
Related
This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 1 year ago.
I'm trying to make a form with React but having some issues with the password and confirm password field.
What I want is basically to have a user type a password and once they confirm, it should display in real time whether the password matches the confirm password field, or if the passwords do not match.
const handlePasswordChange = (event) => {
const { name, value } = event.target;
setCustomerData(prev => ({
...prev,
[name]: value
}));
// setPasswordError displays the error on the form to the user
if(customer.password === customer.confirmPassword) {
setPasswordError('passwords match')
} else if(customer.password !== customer.confirmPassword) {
setPasswordError('Passwords dont match')
} else {
setPasswordError('');
}
console.log("password: " + customer.password);
console.log("Confirm password: " + customer.confirmPassword);
}
State Object
const [customer, setCustomerData] = useState({
firstName: "",
lastName: "",
emailAddress: "",
mobileNumber: "",
dateOfBirth: new Date(),
password: "",
confirmPassword: ""
});
// Displays error using spring boot on the backend
----------
const [firstNameError, setFirstNameError] = useState("");
const [lastNameError, setLastNameError] = useState("");
const [emailAddressError, setEmailAddressError] = useState("");
const [mobileNumberError, setMobileNumberError] = useState("");
const [dateOfBirthError, setDateOfBirthError] = useState("");
const [passwordError, setPasswordError] = useState("");
Form password and confirm password fields
{/*Password*/}
<div className="form-group">
<label htmlFor="password" className="register-form-labels">Password</label>
{passwordError ? <span className="field-validation-styling">{passwordError}</span> : ''}
<input type={passwordShown ? "text" : "password"}
onFocus={(e) => setPasswordError('')}
className="shadow-none form-control register-form-input-field"
name="password"
placeholder="Enter Password"
value={customer.password}
onChange={handlePasswordChange}
/>
</div>
{/*Confirm Password*/}
<div className="form-group">
<label htmlFor="confirmPassword" className="register-form-labels">Confirm Password</label>
<input type={passwordShown ? "text" : "password"}
minLength="8"
className="shadow-none form-control register-form-input-field"
name="confirmPassword"
placeholder="Confirm Password"
value={customer.confirmPassword}
onChange={handlePasswordChange} />
</div>
But when i type in my password, the input is a character behind. This is what i mean
So useState doesn't update immediately, you need something to listen to the changes and update afterwards.
As you are calling handlePasswordChange on change, and then in the same function checking for equality, the customer state is still the "old" state. It wont become the "new" state until reload.
The use of useEffect would be fine here, listening for changes in the customer object, and then acting upon them;
// Function to set passwords
const handlePasswordChange = (event) => {
const { name, value } = event.target;
setCustomerData((prev) => ({
...prev,
[name]: value
}));
// setPasswordError displays the error on the form to the user
console.log("password: " + customer.password);
console.log("Confirm password: " + customer.confirmPassword);
};
//useEffect with a dependency of customer
useEffect(
(_) => {
checkPassword(customer);
},
[customer]
);
// separate check password function
const checkPassword = (customer) => {
if (customer.password === customer.confirmPassword) {
setPasswordError("passwords match");
} else if (customer.password !== customer.confirmPassword) {
setPasswordError("Passwords dont match");
} else {
setPasswordError("");
}
};
So I am trying to make a textfield component in React that is highly reusable but whenever I try to access event.target.name or event.target.value I get empty data.
Is there any way to get this code to work?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name='react'
onChange={e => props.change(e)}
/>
</label>
)
}
return (
<form onSubmit={printValues}>
<TextField
value={form.username}
name='username'
change={updateField}
/>
<br />
<TextField
value={form.password}
name='password'
change={updateField}
/>
<br />
<button>Submit</button>
</form>
);
}
This code is an example that I have gotten to work. Why does this code work but not the code above?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
return (
<form onSubmit={printValues}>
<label>
Username:
<input
value={form.username}
name="username"
onChange={updateField}
/>
</label>
<br />
<label>
Password:
<input
value={form.password}
name="password"
type="password"
onChange={updateField}
/>
</label>
<br />
<button>Submit</button>
</form>
);
}
your child component has it's name prop hardcoded name='react' and that's because your [e.target.name]: e.targe.value statement is not working, use name={props.name} instead and it would solve the problem.
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name={props.name}
onChange={e => props.change(e)}
/>
</label>
)
}
The first code you show has a prop change instead of the onChange event listener in the TextField component.
I was looking here https://material-ui.com/es/components/text-fields/ because I never used material-ui, and in the example they still use the regular onChange.
So try changing change for onChange, as it is the only difference between both code examples besides the use of the component.
I am trying to build a registration form, I have built the login form and it works perfectly, but the registration form is quite off.
First I used the react-dev tools to inspect what is going on and I realized that each input value coming from the registration form happens to be in an array.
I went back to the login form to inspect and saw that the value of the input fields is in the right format (strings). But the value from each specific input field in the registration form is in an array.
What could I be doing wrong?
I tried to replicate what I did in the login form in the registration form, but it is still coming in an array. Also, should hooks be kept in its own separate file?
This is what I see from the react-dev tools for the registration form.
Here is a code snippet of what I did.
const Signup = (props) => {
const authContext = useContext(AuthContext);
const { register, clearErrors, isAuthenticated, error } = authContext;
const [loadBtn, updateLoadBtn] = useState(false);
const [user, setUser] = useState({
firstName: "",
lastName: "",
email: "",
password: "",
username: "",
phoneNumber: "",
});
useEffect(() => {
if (isAuthenticated) {
successMessage();
props.history.push("/dashboard");
}
if (error) {
missingValue(error);
updateLoadBtn(false);
clearErrors();
}
//eslint-disable-next-line
}, [isAuthenticated, error, props.history]);
const { firstName, lastName, email, password, username, phoneNumber } = user;
const onChange = (e) =>
setUser({ ...user, [e.target.name]: [e.target.value] });
const onSubmit = (e) => {
e.preventDefault();
updateLoadBtn(true);
if (
!firstName ||
!lastName ||
!email ||
!password ||
!username ||
!phoneNumber
) {
missingValue("Please enter all fields");
updateLoadBtn(false);
clearErrors();
} else {
register({
firstName,
lastName,
email,
password,
username,
phoneNumber,
});
}
};
return (
<Fragment>
<ToastContainer />
<RegContainer className="container-fluid py-4">
<RegInfo />
<RegColumn
onChange={onChange}
onSubmit={onSubmit}
firstName={firstName}
lastName={lastName}
email={email}
password={password}
phoneNumber={phoneNumber}
loadBtn={loadBtn}
/>
</RegContainer>
</Fragment>
);
}
That is the file responsible for handling the registration.
Here is the custom component
const RegColumn = ({
firstName,
onSubmit,
onChange,
lastName,
username,
password,
phoneNumber,
email,
loadBtn,
}) => {
const bodyStyle = document.querySelector("body").style;
bodyStyle.backgroundImage = "linear-gradient(to bottom, #F6F6F2, #C2EDCE)";
bodyStyle.backgroundRepeat = "no-repeat";
bodyStyle.overflow = "hidden";
bodyStyle.height = "100%";
bodyStyle.fontFamily = "Rubik, sans-serif";
return (
<Fragment>
<div id="reg-column">
<h3 style={{ color: "#388087" }}>REGISTRATION</h3>
<Form divid="form-row1-label" onSubmit={onSubmit}>
<LabelContainer id="form-row1-label">
<LabelContainer id="firstNameLabel">
<LabelInfo labelfor="firstName" labeltitle="First Name" />
</LabelContainer>
<LabelContainer id="lastNameLabel">
<LabelInfo labelfor="lastName" labeltitle="Last Name" />
</LabelContainer>
</LabelContainer>
<InputContainer id="form-row1-input">
<InputContainer id="firstNameInput">
<Input
type="text"
name="firstName"
value={firstName}
id="firstName"
onChange={onChange}
/>
</InputContainer>
<InputContainer id="lastNameInput">
<Input
type="text"
onChange={onChange}
name="lastName"
value={lastName}
id="lastName"
/>
</InputContainer>
// ...
Thank you.
Within your Signup form, you have this...
// ...
const onChange = (e) =>
setUser({ ...user, [e.target.name]: [e.target.value] });
What's happening above?
The onChange() is responsible for updating the values to be submitted.
Within setUser, you are passing a value as a literal array using [e.target.value]
Solution:
Remove the literal array [] and pass value as it's received i.e. e.target.value
The left hand side, e.target.name, is fine since you are actually using a computed property name.
You can also read more about handling forms in react.
I had a component using classBased react, and it was working fine, I decided to switch to usestate and it stopepd working. Now the handlechange records the state in a random manner, but it doesnt work properly
My handlechange and the useState
const [state, setState] = useState({
email: "",
password: "",
wrongCombo: false,
serverError: false
})
const handleChange = (evt) => {
const target = evt.target;
const value = target.value;
const name = target.name;
console.log(name)
console.log(value)
setState({
[name]: value
});
}
My handlesubmit (it detects random values in that console.log, icant find the logic, but the log wont get both values as per inputed in the handlechange)
const handleSubmit = (event) => {
event.preventDefault();
const { email, password } = state;
console.log(state)
props.login(email, password, "login").
then(data => {
}).catch((err) => {
if (err == "Error: Request failed with status code 403") {
setState({
wrongCombo: true
}, () => {
})
} else if (err == "Network error") {
setState({
serverError: true
})
}
})
}
And this is my render
<div>
<form>
{state.wrongCombo ? <Alert variant="danger" dismissible onClose={handleDismiss}> Wrong email and password combination </Alert> : null}
{state.serverError ? <Alert variant="danger" dismissible onClose={handleDismiss}> Server Error </Alert> : null}
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" onChange={handleChange} />
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" name="password" class="form-control" id="exampleInputPassword1" placeholder="Password" onChange={handleChange} />
</div>
<div className="text-center buttonContainer">
<button type="submit" class="btn btn-primary buttonLogin" onClick={handleSubmit}>Submit</button>
</div>
</form>
</div>
From the docs on useState:
unlike this.setState in a class, updating a state variable always replaces it instead of merging it.
You must replace all values in a useState object when updating them. You are only providing an object to the updater with one of the keys, so only that key will be retained.
A simple pattern for doing this would be to spread the previous state before passing your update:
setState(prev => ({...prev, [newKey]: newVal }));
I'm having a syntax doubt on how to update React state using hooks in 2 situations.
1) I have a state called company and a form that fills it up. In contact section, there are two inputs referring to the company employee (name and telephone number). But if the company has more than one employee to be contacted, there is an "Add More Contact" button, which must duplicate the same inputs (of course, aiming to a second contact). How can I do that? I mean, to generate another index in the array "contacts" inside the state, increment the totalOfContacts inside the object that has that array and create the input tags so user can type the second contact's data?
2) When I type any inputs, the code triggers the handleChange function. The "name" and "city" already update the state because they are simple states. But how can I update the contact name and his telephone number, since they are part of an index of an array inside the state?
The code below is already working and my 2 questions are exactly the two commented lines (lines 20 and 29).
The "Save" button simply console.log the results so we can monitor them.
Thanks for now.
import React, { useState, useEffect } from "react";
export default () => {
const [company, setCompany] = useState({
name: "", city: "",
contact: {
totalOfContact: 1,
contacts: [
{id: 0, contactName: "", telephoneNumber: ""}
]
}
})
useEffect(() => {
console.log("teste");
})
const handleChange = item => e => {
if (item === "contactName" || "telephone") {
// How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
} else {
setCompany({ ...company, [item]: e.target.value })
}
}
const handleClick = (e) => {
e.preventDefault();
if (e.target.value === "add") {
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
} else {
console.log(`The data of the company is: ${company}`);
}
}
return (
<div>
<form>
<h3>General Section</h3>
Name: <input type="text" onChange = {handleChange("name")} value = {company.name} />
<br />
City: <input type="text" onChange = {handleChange("city")} value = {company.city} />
<br />
<hr />
<h3>Contacts Section:</h3>
Name: <input type="text" onChange = {handleChange("contactName")} value = {company.contact.contacts[0].name} />
Telephone Numer: <input type="text" onChange = {handleChange("telephone")} value = {company.contact.contacts[0].telephoneNumber} />
<br />
<br />
<button value = "add" onClick = {(e) => handleClick(e)} >Add More Contact</button>
<br />
<br />
<hr />
<button value = "save" onClick = {(e) => handleClick(e)} >Save</button>
</form>
</div>
)
}
To update the state value, you can use functional setState,
const handleChange = item => e => {
//Take the value in a variable for future use
const value = e.target.value;
if (item === "contactName" || "telephone") {
setCompany(prevState => ({
...prevState,
contact: {...prevState.contact, contacts: prevState.contact.contacts.map(c => ({...c, [item]: value}))}
}))
} else {
setCompany({ ...company, [item]: e.target.value })
}
}
To add new set of input on the click of button you can do this,
const handleClick = (e) => {
e.preventDefault();
//This is new set of input to be added
const newSetOfInput = {id: company.contact.contacts.length, contactName: "", telephoneNumber: ""}
if (e.target.value === "add") {
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
setCompany(prevState => ({
...prevState,
contact: {...prevState.contact, contacts: prevState.contact.contacts.concat(newSetOfInput), totalOfContact: prevState.contact.contacts.length + 1}
}))
} else {
console.log(`The data of the company is: ${company}`);
}
}
Finally you need to iterate over your contacts array like,
{company.contact.contacts && company.contact.contacts.length > 0 && company.contact.contacts.map(contact => (
<div key={contact.id}>
Name: <input type="text" onChange = {handleChange("contactName")} value = {contact.contactName} />
<br/>
Telephone Numer: <input type="text" onChange = {handleChange("telephoneNumber")} value = {contact.telephoneNumber} />
</div>
))}
Demo
Note: You should use block elements like div instead of breaking the line using <br/>
To answer your question let us scope down this problem to a much simpler problem, which is how to handle array of contacts.
You just need know the following things:
Map function
How to update array without mutating the original array
I'll use TypeScript so you can understand better.
const [state, setState] = React.useState<{
contacts: {name: string}[]
}>({contacts: []})
return (
<div>
{state.contacts.map((contact, index) => {
return (
<div>
Name:
<input value={contact.name} onChange={event => {
setState({
...state,
contacts: state.contacts.map((contact$, index$) =>
index === index$
? {...contact$, name: event.target.value}
: {...contact$}
)
})
}}/>
</div>
)
}}
</div>
)
Also, this kind of problem is fairly common in React, so understand and memorize this pattern will help you a lot.
You can do something like this.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [company, setCompany] = useState({
name: "",
city: "",
contact: {
totalOfContact: 1,
contacts: [{id: 0, contactName: "", telephoneNumber: ""}]
}
});
console.log(company);
useEffect(() => {
console.log("teste");
}, []);
const handleChange = (item, e,index) => {
if (item === "contactName" || item === "telephoneNumber") {
const contactsNew = [...company.contact.contacts];
contactsNew[index] = { ...contactsNew[index], [item]: e.target.value };
setCompany({
...company,
contact: { ...company.contact, contacts: contactsNew }
});
// How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
} else {
setCompany({ ...company, [item]: e.target.value });
}
};
const handleClick = e => {
e.preventDefault();
if (e.target.value === "add") {
const contactNew = {...company.contact};
contactNew.totalOfContact = contactNew.totalOfContact + 1;
contactNew.contacts.push({id:contactNew.totalOfContact -1, contactName: "", telephoneNumber: ""});
setCompany({...company, contact: {...contactNew}});
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
} else {
alert("Push company to somewhere to persist");
console.log(`The data of the company is: ${company}`);
}
};
return (
<div>
<form>
<h3>General Section</h3>
Name:{" "}
<input
type="text"
onChange={(e) => handleChange("name", e)}
value={company.name}
/>
<br />
City:{" "}
<input
type="text"
onChange={(e) => handleChange("city", e)}
value={company.city}
/>
<br />
<hr />
<h3>Contacts Section:</h3>
{company.contact.contacts.map((eachContact, index) => {
return <React.Fragment>
Name:{" "}
<input
type="text"
onChange={(e) => handleChange("contactName",e, index)}
value={eachContact.name}
/>
Telephone Numer:{" "}
<input
type="text"
onChange={(e) => handleChange("telephoneNumber",e, index)}
value={eachContact.telephoneNumber}
/>
<br />
</React.Fragment>
})}
<br />
<button value="add" onClick={e => handleClick(e)}>
Add More Contact
</button>
<br />
<br />
<hr />
<button value="save" onClick={e => handleClick(e)}>
Save
</button>
</form>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Your state structure looks like an ideal candidate for useReducer hook. I would suggest you try that instead of useState. Your code should look muck readable that way, I suppose. https://reactjs.org/docs/hooks-reference.html#usereducer