I'm using Formik and Yup when I create an entity (company in this case) in order to check if the fields are correct and all of them are introduced (all are required).
When I create an entity it works fine: it only lets you create it if all fields are introduced and the rules are fulfilled (only one rule at the moment for the email).
This is the code which works to create a new company with 2 fields name and email:
import React from 'react';
import { Redirect } from 'react-router-dom';
import { Formik, Form, Field } from 'formik';
import { Input, Button, Label, Grid } from 'semantic-ui-react';
import { connect } from 'react-redux';
import * as Yup from 'yup';
import { Creators } from '../../../actions';
import Layout from '../../Layout/Layout';
class CreateCompanyForm extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
name: '',
contactMail: '',
redirectCreate: false,
redirectEdit: false,
edit: false,
};
}
componentDidMount() {
const {
getCompany,
location: { pathname },
} = this.props;
}
handleSubmit = values => {
const { createCompany, getCompanies } = this.props;
createCompany(values);
this.setState({ redirectCreate: true });
getCompanies(this.props.query);
};
render() {
let title = 'Create Company';
let buttonName = 'Create';
let submit = this.handleSubmitCreate;
const { redirectCreate, redirectEdit } = this.state;
if (redirectCreate) {
return <Redirect to="/companies" />;
}
const initialValues = {
name: '',
contactMail: '',
};
const requiredErrorMessage = 'This field is required';
const emailErrorMessage = 'Please enter a valid email address';
const validationSchema = Yup.object({
name: Yup.string().required(requiredErrorMessage),
contactMail: Yup.string()
.email(emailErrorMessage)
.required(requiredErrorMessage),
});
return (
<Layout>
<div>
<Button type="submit" form="amazing">
Create company
</Button>
<Button onClick={() => this.props.history.goBack()}>Discard</Button>
<div>Create company</div>
</div>
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ values, errors, touched, setValues }) => (
<Form id="amazing">
<Grid columns={2}>
<Grid.Column>
<Label>Company Name</Label>
<Field name="name" as={Input} placeholder="Hello" />
<div>{touched.name && errors.name ? errors.name : null}</div>
</Grid.Column>
<Grid.Column>
<Label>Contact Mail</Label>
<Field
name="contactMail"
as={Input}
placeholder="johnappleseed@hello.com"
/>
<div>
{touched.contactMail && errors.contactMail
? errors.contactMail
: null}
</div>
</Grid.Column>
</Grid>
</Form>
)}
</Formik>
</Layout>
);
}
}
const mapStateToProps = state => ({
companies: state.companies.companies,
company: state.companies.selectedCompany,
query: state.companies.query,
});
const mapDispatchToProps = {
getCompanies: Creators.getCompaniesRequest,
createCompany: Creators.createCompanyRequest,
getCompany: Creators.getCompanyRequest,
updateCompany: Creators.updateCompanyRequest,
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateCompanyForm);
The problem appears when I want to edit the company. So when someone click on a company on edit button it should open the company with all its fields containing the current values which should be editable.
To get those current values I'm using the state, for example the email can be accessed from this.state.email and in order to change the value it was added onChange method.
The values can be modified in the text input. However, it triggers the Yup message that says the field is required even if there is data in it - why is this happening? The field is not empty, that's the situation when it must show that message.
And of course, it doesn't update the entity when I click to save it because it requires those fields.
Here is the code:
import React from 'react';
...
class CreateCompanyForm extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
name: '',
contactMail: '',
redirectCreate: false,
redirectEdit: false,
edit: false,
};
}
componentDidMount() {
const {
getCompany,
location: { pathname },
} = this.props;
if (pathname.substring(11) !== 'create') { // checks the URL if it is in edit mode
getCompany(pathname.substring(16));
this.setState({
edit: true,
});
this.setState({
name: this.props.company.name,
contactMail: this.props.company.contactMail,
});
}
}
onChange = (e, { name, value }) => { // method to update the state with modified value in input
this.setState({ [name]: value });
};
handleSubmit = values => {
const { createCompany, getCompanies } = this.props;
createCompany(values);
this.setState({ redirectCreate: true });
getCompanies(this.props.query);
};
handleSubmitEdit = e => {
e.preventDefault();
const { name, contactMail } = this.state;
const { updateCompany } = this.props;
updateCompany(this.props.company._id, {
name,
contactMail,
});
this.setState({ redirectEdit: true });
};
render() {
let title = 'Create Company';
let buttonName = 'Create';
let submit = this.handleSubmitCreate;
const { redirectCreate, redirectEdit } = this.state;
if (redirectCreate) {
return <Redirect to="/companies" />;
}
if (redirectEdit) {
return <Redirect to={`/companies/${this.props.company._id}`} />;
}
if (this.state.edit) {
title = 'Edit Company';
buttonName = 'Edit';
submit = this.handleSubmitEdit;
}
const initialValues = {
name: '',
contactMail: '',
};
const requiredErrorMessage = 'This field is required';
const emailErrorMessage = 'Please enter a valid email address';
const validationSchema = Yup.object({
name: Yup.string().required(requiredErrorMessage),
contactMail: Yup.string()
.email(emailErrorMessage)
.required(requiredErrorMessage),
});
return (
<Layout>
<div>
<Button type="submit" form="amazing">
Create company
</Button>
<Button onClick={() => this.props.history.goBack()}>Discard</Button>
<div>Create company</div>
</div>
<Formik
htmlFor="amazing"
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={values => this.handleSubmit(values)}>
{({ values, errors, touched, setValues }) => (
<Form id="amazing">
<Grid columns={2}>
<Grid.Column>
<Label>Company Name</Label>
<Field
name="name"
as={Input}
placeholder="Hello"
value={this.state.name || ''} // takes the value from the state
onChange={this.onChange} // does the changing
/>
<div>{touched.name && errors.name ? errors.name : null}</div>
</Grid.Column>
<Grid.Column>
<Label>Contact Mail</Label>
<Field
name="contactMail"
as={Input}
placeholder="johnappleseed@hello.com"
value={this.state.contactMail || ''} // takes the value from the state
onChange={this.contactMail} // does the changing
/>
<div>
{touched.contactMail && errors.contactMail
? errors.contactMail
: null}
</div>
</Grid.Column>
</Grid>
</Form>
)}
</Formik>
</Layout>
);
}
}
...
export default connect(mapStateToProps, mapDispatchToProps)(CreateCompanyForm);
Any ideas about how to solve this and make the fields editable and remove the 'This field is required' message when the field already has data?