I have the following component that makes a form and use formik for form validation and has a custom input field created with react-places-autocomplete for entering an address to the form. the form is working fine, However the validation is not showing even though the address field is required, When I make it empty the error validation from formik is not showing.
Below is the code for the components:
//FormikErrorLabel component
import React from 'react';
const FormikErrorLabel = ({ error, children, ...props }) => {
return <label {...props}>{children}</label>
}
export default FormikErrorLabel;
//FormikErrorLabel component
import React from 'react';
const FormikInputFeedback = ({ children }) => (
<span className="text-danger">{children}</span>
)
export default FormikInputFeedback;
//FormikPlacesAutoComplete custom input places auto complete component with formik validation
import React, { Component } from "react";
import classnames from "classnames";
import FormikErrorLabel from "./FormikErrorLabel";
import FormikInputFeedback from "./FormikInputFeedback";
import apiKey from "../../configureMap";
import Script from "react-load-script";
import PlacesAutocomplete, {
geocodeByAddress,
getLatLng
} from "react-places-autocomplete";
const styles = {
autocompleteContainer:{
zIndex:1000
}
}
class FormikPlacesAutoComplete extends Component {
constructor(props) {
super(props);
this.state = {
address: '',
scriptLoaded:false
};
}
handleScriptLoad = () => {
this.setState({scriptLoaded:true});
};
handleChange = address => {
this.setState(()=>{
this.props.form.setFieldValue('coordinates',address)
return {address};
});
};
handleSelect = address => {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(latLng => {
console.log('Success', latLng);
this.setState(()=>{
this.props.form.setFieldValue('coordinates',address)
return {address};
});
})
.catch(error => console.error('Error', error));
};
render() {
const {
field: { name, ...field }, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
className,
label,
...props
} = this.props;
const error = errors[name];
const touch = touched[name];
const classes = classnames(
"form-group",
{
"animated shake error": !!error
},
className
);
console.log("props", props);
return (
<React.Fragment>
<Script
url={`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`}
onLoad={this.handleScriptLoad}
/>
{ this.state.scriptLoaded &&
<div className={classes}>
<FormikErrorLabel htmlFor={name} error={error}>
{label}
</FormikErrorLabel>
<PlacesAutocomplete
name={name}
id={name}
{...field}
{...props}
// onChange={(selectValue) => this.setState(() => {
// this.props.form.setFieldValue('categories',selectValue)
// return { selectValue }
// })}
value={this.state.address}
onChange={this.handleChange}
onSelect={this.handleSelect}
// className="form-control"
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input
{...getInputProps({
placeholder: 'Search Places ...',
className: 'location-search-input form-control',
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => {
const className = suggestion.active
? 'suggestion-item--active'
: 'suggestion-item';
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: '#fafafa', cursor: 'pointer' }
: { backgroundColor: '#ffffff', cursor: 'pointer' };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style,
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
{touch && error && <FormikInputFeedback>{error}</FormikInputFeedback>}
</div>
}
</React.Fragment>
);
}
}
export default FormikPlacesAutoComplete;
//Form component
import React, { Component } from "react";
import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
import { connect } from "react-redux";
import { actions as locationActions } from "../../duckes/locations";
import { getElementByID } from "../../utils";
import toastr from "toastr";
import { Formik, Form, Field } from 'formik'
import { object, string, array } from 'yup';
import isEmpty from 'lodash/isEmpty'
import FormikTextInput from "../common/FormikTextInput";
import FormikSelectInput from "../common/FormikSelectInput";
import FormikPlacesAutoComplete from "../common/FormikPlacesAutoComplete";
class ManageLocationPage extends Component {
render() {
})
return (
<Formik
validationSchema={object().shape({
coordinates: string()
.required('Coordinates is required.')
})}
initialValues={
{...this.props.location }
}
onSubmit={(values, actions) => {
console.log('form values:',values)
}}
render={({errors, dirty, isSubmitting, values, setFieldValue}) => (
<Form>
<h3 className="my-5 text-capitalize">Manage Location</h3>
<Field
type="text"
name="coordinates"
label="Coordinates"
component={FormikPlacesAutoComplete}
/>
<button
type="submit"
className="btn btn-default"
disabled={isSubmitting || !isEmpty(errors) || !dirty}
>
Save
</button>
</Form>
)}
/>
);
}
}
//Prop Types validation
ManageLocationPage.propTypes = {
location: PropTypes.object.isRequired,
categories: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
//Redux connect
const mapStateToProps = ({ locations, categories }, ownProps) => {
let location = {
...
coordinates: ""
};
return {
location: getElementByID(....) || location,
};
};
const mapDispatchToProps = dispatch => {
return {
actions: bindActionCreators(locationActions, dispatch)
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ManageLocationPage);
My code is based on react-places-autocomplete example, And this article.
How to make the formik validation errors to show?
It seems that there is something that maybe I am missing about formik validation, When I clear the address field in the GUI and debug my FormikPlacesAutoComplete onChange handler:
handleChange = address => {
this.setState(()=>{
this.props.form.setFieldValue('address',address);
this.props.form.setFieldValue('latLng',{lat:null,lng:null})
return {address};
});
};
I see that after the lines 3,4 when I check the form values in the debugger log: this.props.form.values.address = "AZ, USA" (instead of "" )
this.props.form.values.latLng = {lat: 34.0489281, lng: -111.09373110000001} (instead of {lat: null, lng: null} )
Formik doesn't react after lines 3,4 maybe I didn't completely understandd how this.props.form.setFieldValue works, I thought setFieldValue will trigger the validation, I will further investigate.