Creating a function that will update my state based to edit user profile information based on input submitted on a form.
My function is only updating the last input I entered in my form. Here is a snippet of my code
let { user,logOutUser,updateProfile } = useContext(AuthContext)
//store users in state
const[profileUser, setProfileUser] = useState({user})
//handle form submission
const handleSubmit = e => {
console.log(user)
const updatedProfile = {
username: profileUser.username,
email: profileUser.email,
first_name: profileUser.first_name,
last_name: profileUser.last_name,
city: profileUser.city,
country: profileUser.country
}
console.log(updatedProfile)
e.preventDefault();
}
and here's my form:
<Form>
<Row>
<Col className="pr-1" md="6">
<FormGroup>
<label>Username</label>
<Input
placeholder="Username"
type="text"
name="username"
onChange={ e => setProfileUser({'username': e.target.value}) }
/>
</FormGroup>
</Col>
<Col className="pr-1" md="6">
<FormGroup>
<label htmlFor="exampleInputEmail1">
Email address
</label>
<Input
placeholder="Email"
type="email"
name="email"
value={profileUser.email}
onChange={ e => setProfileUser({'email': e.target.value}) }
/>
</FormGroup>
</Col>
</Row>
<Row>
<Col className="pr-1" md="6">
<FormGroup>
<label>First Name</label>
<Input
placeholder="Enter your first name"
type="text"
name="first_name"
onChange={ e => setProfileUser({'first_name': e.target.value}) }
/>
</FormGroup>
</Col>
<Col className="pr-1" md="6">
<FormGroup>
<label>Last Name</label>
<Input
placeholder="Enter your last name"
type="text"
name="last_name"
onChange={ e => setProfileUser({'last_name': e.target.value}) }
/>
</FormGroup>
</Col>
</Row>
<Row>
<Col className="pr-1" md="6">
<FormGroup>
<label>City</label>
<Input
placeholder="Enter your city"
type="text"
name="city"
onChange={ e => setProfileUser({'city': e.target.value}) }
/>
</FormGroup>
</Col>
<Col className="pr-1" md="6">
<FormGroup>
<label>Country</label>
<Input
placeholder="Enter your country"
type="text"
name="country"
onChange={ e => setProfileUser({'country': e.target.value}) }
/>
</FormGroup>
</Col>
<div className="update ml-auto mr-auto">
<Button
className="btn-round"
color="primary"
type="submit"
onClick={handleSubmit}
>
Update Profile
</Button>
</div>
</Row>
</Form>
This is what I have in users:
bio: ""
city: ""
country: ""
exp: *****
first_name: ""
iat: *****
jti: "******"
last_name: ""
photo: "\"profile/2022/03/27/emmanuelpic.png\""
token_type: "access"
user_id: 1
username: "emmanuelS21"
When I console log updateProfile after submitting updated profile data, I am only getting the last input I entered in my form (ie. If I enter last_name, I only see this in my console log while everything else in my object is empty)
Each use of setProfileUser is overwriting the entire object with a new object that contains only one property:
onChange={ e => setProfileUser({'email': e.target.value}) }
You can destructure the current state to build the complete new state:
onChange={ e => setProfileUser({ ...profileUser, 'email': e.target.value}) }
This is notably different from the older setState used in class-based components. With setState the framework would automatically perform partial updates for you. But with the setter function in useState hooks, any update is complete as-is.
when you set the state must add rest of value to new state with {...rest}
onChange={ e => setProfileUser({...profileUser,'username': e.target.value}) }
Related
I am creating a form using react-bootstrap, react-hook-form, and yup for validation.
I have several fields that are not required like url and radio.
I have used a custom regex instead of yup's url() as it is really strict, for example not accepting google.com or www.google.com.
Also, there are radio type fields like offerInternships, offerVisaSponsorships, offerRemoteWorking which I have declared as notRequired in yup's schema.
The problem is when I press submit button, errors are shown indicating that they must be filled, and I only be able to proceed if I filled them with correct values.
I am not really sure what could have gone wrong here.
Edit 1: I have modified the regex to accept empty strings by adding |^$ at the end of re, and the url now can accept empty strings and act as if it's not required (I am not sure if this actually fixes the problem), but the radio type's problem still exists.
import { Container, Row, Col, Button, Form } from "react-bootstrap";
import * as yup from "yup";
import { useForm } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
/**
* Company's validation schema.
*/
const re =
/^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/gm;
const formSchema = yup.object().shape({
url: yup.string().matches(re, "Enter a valid URL!").required(),
name: yup.string().required(),
location: yup.object().shape({
country: yup.string().required(),
state: yup.string().required(),
city: yup.string().required(),
address: yup.string().required(),
}),
offerInternships: yup.bool().default(false).notRequired(),
offerRemoteWork: yup.bool().default(false).notRequired(),
offerVisaSponsorship: yup.bool().default(false).notRequired(),
socialMedia: yup.object().shape({
Linkedin: yup.string().matches(re, "Enter a valid URL!").notRequired(),
GitHub: yup.string().matches(re, "Enter a valid URL!").notRequired(),
}),
});
const RequestForm = () => {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm({
resolver: yupResolver(formSchema),
mode: "all",
});
/**
* Handling the submittion of the form
*/
const onSubmit = async (data) => {
try {
console.log(errors);
console.log(data);
} catch (error) {
console.log(error);
}
};
return (
<>
<Container id="RequestForm-UserForm" className="mt-5 outer-border p-5">
<Form onSubmit={handleSubmit(onSubmit)}>
<Row id="company-details-row">
<Col id="company-details-row-input" sm={12} md={9}>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Company's name</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Microsoft"
{...register("name")}
isInvalid={!!errors?.name}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Company's website</Form.Label>
<Form.Control
type="text"
placeholder="e.g. https://www.microsoft.com"
{...register("url")}
isInvalid={!!errors.url}
/>
</Form.Group>
<h3 className="mb-0">Location</h3>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Country</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Egypt, United States of America,...etc."
{...register("location.country")}
isInvalid={!!errors.location?.country}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>State/Governorate</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Cairo, California, Berlin...etc."
{...register("location.state")}
isInvalid={!!errors.location?.state}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>City</Form.Label>
<Form.Control
type="text"
placeholder="e.g. Maadi, San Francisco,...etc."
{...register("location.city")}
isInvalid={!!errors.location?.city}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>Address</Form.Label>
<Form.Control
type="text"
placeholder="e.g. 14th Zaki Sayed St."
{...register("location.address")}
isInvalid={!!errors.location?.address}
/>
</Form.Group>
<h3 className="mt-2 mb-0">Social Media</h3>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>
<i className="icon ion-social-linkedin"></i> Linkedin
</Form.Label>
<Form.Control
type="text"
placeholder="e.g. https://www.linkedin.com/company/x"
{...register("socialMedia.Linkedin")}
isInvalid={!!errors.socialMedia?.Linkedin}
/>
</Form.Group>
<Form.Group className="mb-2" controlId="formBasicEmail">
<Form.Label>
<i className="icon ion-social-github"></i> GitHub
</Form.Label>
<Form.Control
type="text"
placeholder="e.g. https://www.github.com/company"
{...register("socialMedia.GitHub")}
isInvalid={!!errors.socialMedia?.GitHub}
/>
</Form.Group>
<h3 className="mt-2 mb-0">Culture & Environment</h3>
<div id="offer-Internship" key={"inline-radio-internships"}>
<Form.Label>Does your company offer internships? π</Form.Label>
<br></br>
<Form.Check
inline
label="Yes"
value={"true"}
{...register("offerInternships", { required: false })}
type={"radio"}
isInvalid={!!errors.offerInternships}
/>
<Form.Check
inline
label="No"
value={"false"}
{...register("offerInternships", { required: false })}
type={"radio"}
isInvalid={!!errors.offerInternships}
/>
</div>
<div id="offer-VisaSponsorship" key={"inline-radio-visa"}>
<Form.Label>
Does your company offer visa sponsorship? βοΈ
</Form.Label>
<br></br>
<Form.Check
inline
label="Yes"
value={"true"}
{...register("offerVisaSponsorship", { required: false })}
type={"radio"}
isInvalid={!!errors.offerVisaSponsorship}
/>
<Form.Check
{...register("offerVisaSponsorship", { required: false })}
inline
label="No"
value={"false"}
type={"radio"}
isInvalid={!!errors.offerVisaSponsorship}
/>
</div>
<div id="offer-remoteWork" key={"inline-radio-remote"}>
<Form.Label>
Does your company offer remote working? πΊοΈ
</Form.Label>
<br></br>
<Form.Check
{...register("offerRemoteWork", { required: false })}
inline
label="Yes"
value={"true"}
type={"radio"}
isInvalid={!!errors.offerRemoteWork}
/>
<Form.Check
{...register("offerRemoteWork", { required: false })}
inline
label="No"
value={"false"}
type={"radio"}
isInvalid={!!errors.offerRemoteWork}
/>
</div>
</Col>
</Row>
<Row id="company-submit text-center">
<Col className={"text-center"} sm={12}>
<h2 className="fs-1 main-color-1-dark">
We have reached to the end of the form! π
</h2>
<Button variant="success" type="submit" className="rubik-font">
Submit form
</Button>
</Col>
</Row>
</Form>
</Container>
</>
);
};
export default RequestForm;
I have a multistep form for signing up. What is the easiest and the best approach for form validation? What should I use? Is it alright to use Formik for that? Could you suggest to me what to do?
Here is one of the forms:
return(
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control type="text" value={formValues.firstName} onChange={handleChange('firstName')} />
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control type="text" value={formValues.lastName} onChange={handleChange('lastName')} />
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control type="email" value={formValues.email} onChange={handleChange('email')} />
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control type="password" value={formValues.password} onChange={handleChange('password')} />
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control type="tel" value={formValues.phone} onChange={handleChange('phone')} />
</Form.Group>
<Button variant="light" type="submit" size="lg" onClick={redirectToHome}>Cancel</Button>
<Button variant="primary" type="submit" size="lg" onClick={saveAndContinue}>Next</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
};
You can use Formik and a Yup for validation schema:
https://formik.org/docs/api/formik#validationschema-schema----schema
npm links:
https://www.npmjs.com/package/formik
https://www.npmjs.com/package/yup
It will look like this:
export default function MyForm() {
const initValues = {
firstName: "",
lastName: "",
email: "",
password: "",
phone: ""
};
const schema = Yup.object().shape({
firstName: Yup.string(),
lastName: Yup.string(),
email: Yup.string().email("Email format is invalid."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/[a-zA-Z]/, "Password can only contain Latin letters."),
phone: Yup.string().phone(" ")
});
return (
<Formik validationSchema={schema} initialValues={initValues}>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
setFieldTouched
} = props;
return (
<Container>
<Row className="justify-content-center">
<Col md="8">
<Card border="dark">
<Card.Title className="text-center">New User</Card.Title>
<Card.Body>
<Form>
<Form.Group controlId="formGridFirstName">
<Form.Label>First Name</Form.Label>
<Form.Control
name="firstName"
type="text"
value={values.firstName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridLastName">
<Form.Label>Last Name</Form.Label>
<Form.Control
name="lastName"
type="text"
value={values.lastName}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlID="formGridEmail">
<Form.Label>Email</Form.Label>
<Form.Control
name="email"
type="email"
value={values.email}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPassword">
<Form.Label>Password</Form.Label>
<Form.Control
name="password"
type="password"
value={values.password}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Form.Group controlId="formGridPhone">
<Form.Label>Phone</Form.Label>
<Form.Control
name="phone"
type="tel"
value={values.phone}
onChange={handleChange}
setFieldTouched={setFieldTouched}
errors={errors}
/>
</Form.Group>
<Button
variant="light"
type="submit"
size="lg"
onClick={redirectToHome}
>
Cancel
</Button>
<Button
variant="primary"
type="submit"
size="lg"
onClick={saveAndContinue}
>
Next
</Button>
</Form>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
);
}}
</Formik>
);
}
But you should change also your Form.Control component:
Add
import { ErrorMessage, getIn } from "formik";
const error = getIn(props.errors, props.name);
and add to your Input these attributes
onChange={(e) => {setFieldTouched("firstName");handleChange(e);}
error={error}
and Error to show
<Error component="div" name={props.name} />
I would suggest adding your own validators and bundling inputs that are of the same type. For example First Name and Last Name are both strings so you can have a validator for string types. You could do it like this
//validator.js
const stringValidator = (value) => {
if(isEmpty(value)){
return { isValid: false, errors: ["Field cannot be blank"] }
}
return { isValid: true, errors: [] }
}
const config = {
firstName: stringValidator,
lastName: stringValidator,
email: emailValidator,
cellphone: cellphoneValidator,
password: passwordValidator
}
const getValidator = (questionId) => config[questionId];
export default (questionId, value) => {
const validate = getValidator(questionId);
return validate(value)
}
...
// Form.js class
answerQuestion(questionId){
return (e) => {
const answer = e.target.value;
const validation = validate(questionId, answer)
const { errors } = validation;
let { fields } = this.state;
fields[questionId] = { errors, value, label: field.label }
this.setState({ fields })
}
}
...
<Form>
{this.state.fields.map( field => {
return(
<Form.Group>
<Form.Label>{field.label}</Form.Label>
<Form.Control type="text" value={field.value} onChange={this.answerQuestion(field.questionId)} />
{field.errors.map(err => <span>{err}</span>}
</Form.Group>
)
}
</Form>
So this may be something simple but I'm hitting a roadblock. I want to take in a form input but can't seem to figure out how to append the value correctly so its appending string is captured.
I'm using Formik with yup in react.
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
Any help would be appreciated. I just want to get the .localhost to be automatically added to the input items. for this field. I thought I could do something like value=({values.domain_url} + ".localhost") but that didn't seem to work and as you may already tell I am very new to javascript.
Thank you!
Full code below, I'm also having issues with datepicker displaying within the formik state, and then there's how to even get the values to push to my getTenant(function) to be passed to my api.
static propTypes = {
addTenant: PropTypes.func.isRequired,
};
onSubmit = (values) => {
values.preventDefault();
this.props.addTenant(values);
};
render() {
const {
domain_url,
schema_name,
name,
config,
} = this.state;
const TenantSchema = Yup.object().shape({
domain_url: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client URL header is required"),
schema_name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client db name is required"),
name: Yup.string()
.max(255, "Must be shorter than 255 characters")
.required("Client name is required"),
});
return (
<div className={s.root}>
<Formik
initialValues={{
domain_url: "",
schema_name: "",
client_name: "",
config: [
{
date: "",
Tenant_description: "",
},
],
}}
// validationSchema={TenantSchema} this is commented off because it breaks
submittions
onSubmit={(values, { setSubmitting, resetForm }) => {
setSubmitting(true);
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
resetForm();
setSubmitting(false);
}, 100);
}}
//onSubmit={onSubmit}
>
{({
values,
errors,
status,
touched,
handleBlur,
handleChange,
isSubmitting,
setFieldValue,
handleSubmit,
props,
}) => (
<FormGroup>
<Form onSubmit={handleSubmit}>
<legend>
<strong>Create</strong> Tenant
</legend>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Show URL
</Label>
<Col md={7}>
<InputGroup>
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onSubmit={(values) => {
values.domain_url = values.domain_url + ".localhost";
}} //this isn't working
className={
"form-control" +
(errors.domain_url && touched.domain_url
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="domain_url"
component="div"
className="invalid-feedback"
/>
<InputGroupAddon addonType="append">
.localhost
</InputGroupAddon>
</InputGroup>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Database Name
</Label>
<Col md={7}>
<Field
name="schema_name"
type="text"
className={
"form-control" +
(errors.schema_name && touched.schema_name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="schema_name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label for="normal-field" md={4} className="text-md-right">
Name
</Label>
<Col md={7}>
<Field
name="name"
type="text"
className={
"form-control" +
(errors.name && touched.name
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="name"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FieldArray
name="config"
render={(arrayHelpers) => (
<div>
{values.config.map((config, index) => (
<div key={index}>
<FormGroup row>
<Label
md={4}
className="text-md-right"
for="mask-date"
>
Tenant Description
</Label>
<Col md={7}>
<TextareaAutosize
rows={3}
name={`config.${index}.tenant_description`}
id="elastic-textarea"
type="text"
onReset={values.event_description}
placeholder="Quick description of tenant"
onChange={handleChange}
value={values.tenant_description}
onBlur={handleBlur}
className={
`form-control ${s.autogrow} transition-height` +
(errors.tenant_description &&
touched.tenant_description
? " is-invalid"
: "")
}
/>
<ErrorMessage
name="tenant_description"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
<FormGroup row>
<Label
for="normal-field"
md={4}
className="text-md-right"
>
Date
</Label>
<Col md={7}>
<DatePicker
tag={Field}
name={`config.${index}.date`}
type="date"
selected={values.date}
value={values.date}
className={
"form-control" +
(errors.date&& touched.date
? " is-invalid"
: "")
}
onChange={(e) =>
setFieldValue("date", e)
}
/>
<ErrorMessage
name="date"
component="div"
className="invalid-feedback"
/>
</Col>
</FormGroup>
</div>
))}
</div>
)}
/>
<div className="form-group">
<button
type="submit"
disabled={isSubmitting}
className="btn btn-primary mr-2"
>
Save Tenant
</button>
<button type="reset" className="btn btn-secondary">
Reset
</button>
</div>
</Form>
<Col md={7}>{JSON.stringify(values)}</Col>
</FormGroup>
)}
</Formik>
</div>
);
}
}
export default connect(null, { addTenant })(TenantForm);
You could use setFieldValue if you want to customize the value
https://jaredpalmer.com/formik/docs/api/formik#setfieldvalue-field-string-value-any-shouldvalidate-boolean--void
You can add onChange
<Field
id="appended-input"
name="domain_url"
type="text"
value={values.domain_url}
onChange={st => {
let value = st.target.value;
let suffix = ".localhost";
let index = value.indexOf(".localhost");
if (index > 0) {
suffix = "";
}
//add suffix 'localhost' if it is not already added
props.setFieldValue("domain_url", value + suffix);
}}
className={
"form-control" +
(errors.domain_url && touched.domain_url ? " is-invalid" : "")
}
/>;
But adding suffix is more preferable on onSubmit:
onSubmit = {(values, actions) => {
console.log('valuesbefore',values)
values.domain_url= values.domain_url+ ".localhost"
console.log('valuesafter',values)
this.props.addTenant(values);
};
So I am trying to populate a form with data from my server. The server is returning just fine. All strings. I want each form field to be populated with the current details from the database.
I tried the following:
setting each state function in useEffect, but this wouldn't allow me
to change form data
setting a default value but this only works for one instance of the
form and then changes back
function Dashboard(props) {
const { classes } = props;
let user = firebase.getCurrentUser();
let id = user.uid;
const [profile, setProfileData] = useState('');
useEffect( () => {
axios.get('https://www.memento-wedding.com/user/' + id)
.then(response => {
setProfileData(response.data);
})
console.log(profile.gender);
});
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('');
const [date, setDate] = useState('');
const [gender, setGender] = useState();
return (
<div className="form">
<h1 className="white center">Welcome, {firebase.getCurrentUsername()}!</h1>
<h1 className="white center">Create a Profile</h1>
<Form onSubmit={e => e.preventDefault() && false}>
<Row form>
<Label for="firstName" sm={3} className="white right">First Name</Label>
<Col sm={6}>
<Input type="text" name="firstName" id="name" value={firstName} onChange={e=>setFirstName(e.target.value)}/>
</Col>
<Col sm={1}></Col>
</Row>
<br></br>
<Row form>
<Label for="firstName" sm={3} className="white right">Last Name</Label>
<Col sm={6}>
<Input type="text" name="firstName" id="name" value={lastName} onChange={e=>setLastName(e.target.value)}/>
</Col>
<Col sm={1}></Col>
</Row>
<br></br>
<Row form>
<Label for="email" sm={3} className="white right">Email</Label>
<Col sm={6}>
<Input type="email" name="email" id="email" disabled value={profile.email}/>
</Col>
</Row>
<br></br>
<Row form>
<Label for="birthday" sm={3} className="white right">Birthday</Label>
<Col sm={6}>
<Input type="date" name="date" id="date" value={date} onChange={e=>setDate(e.target.value)}/>
</Col>
</Row>
<br></br>
<Row>
<Label for="gender" sm={3} className="white right">Gender</Label>
<Col md={5} sm={7}>
<RadioButtons gender={gender} setGender={e=>setGender(e.target.value)} />
</Col>
</Row>
</Form>
)
}
Please add filter condition []
useEffect( () => {
axios.get('https://www.memento-wedding.com/user/' + id)
.then(response => {
setProfileData(response.data);
})
console.log(profile.gender);
},[]);
First am pretty new to react.
Am trying to change state of form inputs as follows
constructor(props) {
super(props);
this.state = {
pwd:'',
email:'',
value:''
};
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
}
handlePasswordChange(event){
//this.setState({value: event.target.value});//Works
this.setState({pwd: event.target.pwd});// Fails
}
My problem is I am not able to set the state of the password field and instead am getting a warning saying Warning: A component is changing a controlled input of type password to be uncontrolled
My render method is as bellow
render(){
return(
<div
className="container col-md-6 col-md-offset-3"
>
<Form horizontal onSubmit={this.handleSubmit}>
<FormGroup
controlId="formHorizontalEmail">
<Col componentClass={ControlLabel} sm={2}>
Email
</Col>
<Col sm={10}>
<FormControl type="email" placeholder="Email"
value={this.state.email} onChange={this.handleEmailChange}/>
</Col>
</FormGroup>
<FormGroup
controlId="formHorizontalPassword"
>
<Col componentClass={ControlLabel} sm={2}>
Password
</Col>
<Col sm={10}>
<FormControl type="password" placeholder="Password"
value={this.state.pwd} onChange={this.handlePasswordChange}
//if I change to value={this.state.value} it works
/>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={2} sm={10}>
<Checkbox>Remember me</Checkbox>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={2} sm={10}>
<Button type="submit">
Sign in
</Button>
</Col>
</FormGroup>
</Form>
</div>
);
}
Note: I have omitted some code that I think is not crucial.
Just change:
this.setState({pwd: event.target.pwd});// Fails
for:
this.setState({pwd: event.target.value});
The event has an attribute target which has an attribute value with the value of the field at that particular moment. Hence you should assign that value to the pwd state attribute that you are defining :)
Always remember that you are sending an object to the setState function. The key of that object is the one that you are defining (pwd). The value is just the thing that you assign to that key, it could be a number, a string... or the value of an object's attribute (like in this case the string that contains event.target.value).
I'll show you a little snippet that I'm using in production:
This is your state:
constructor(props) {
super(props);
this.state = {
pwd:'',
email:''
};
So, now you may want to build a form in order to change these values. I'd recommend to do so:
<input name="pwd" onChange={(e) => this.handleChange(e)} ...>
<input name="email" onChange={(e) => this.handleChange(e)} ...>
So now you can do so:
handleChange(e){
this.setState({[e.target.name]: e.target.value})
}
e.target.name takes the name of your input field (in that case: 'pwd' or 'email') and e.target.value takes its value.
When you're writing more complex forms, this can help you a lot!