Formik验证不适用于我的自定义react-places-autocomplete组件

时间:2018-07-12 12:17:18

标签: reactjs googleplacesautocomplete formik

我具有以下组件,该组件可以制作表单并使用formik进行表单验证,并具有使用react-places-autocomplete创建的用于输入表单地址的自定义输入字段。窗体工作正常,但是即使需要地址字段,验证也不会显示。当我将其设为空时,不会显示来自formik的错误验证。

下面是组件的代码:

//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);

我的代码基于react-places-autocomplete的示例,而这个article

如何显示formik验证错误?

似乎关于Formik验证我可能缺少一些东西, 当我在GUI中清除地址字段并调试FormikPlacesAutoComplete onChange处理程序时:

  1. handleChange =地址=> {
  2. this.setState(()=> {
  3. this.props.form.setFieldValue('address',address);
  4. this.props.form.setFieldValue('latLng',{lat:null,lng:null})
  5. 返回{地址};
  6. });
  7. };

当我在调试器日志中检查表单值时,我看到在第3,4行之后: this.props.form.values.address =“ AZ,USA”(而不是“”)

this.props.form.values.latLng = {lat:34.0489281,lng:-111.09373110000001}(而不是{lat:null,lng:null})

在第3,4行之后,Formik没有反应,也许我不完全了解this.props.form.setFieldValue的工作方式, 我以为setFieldValue将触发验证,我将进一步调查。

2 个答案:

答案 0 :(得分:1)

可能要晚了,但供以后参考....;-)

您缺少setFieldTouched调用。 仅在触摸该字段时,ErrorMessage才会显示错误。

我创建了一个像您一样的组件(部分复制了一些代码... CodePen Link

仍然需要一些工作。

当前,自动填充字段的结构包含值,地址和经度/纬度。

Onchange修改值(需要进行验证,因此需要location.value的setTouched) OnSelect修改地址和经纬度。如果地址为空,则表示输入的值与地址不匹配... location.address的setTouched。

答案 1 :(得分:1)

我遇到了这个问题,但是解决方案非常明显。 Places Autocomplete类似于嵌套对象,因此要对其进行验证,需要使用getIn(),它是嵌套对象的formik验证功能。并且您应该传递object.value,在我的情况下是名称。

验证模式如下:

const validationSchema = yup.object({
    legalAddress: yup.object().shape({
        addressLine: yup.string().required('Required')
})
import { Form, InputGroup } from 'react-bootstrap';
import { getIn } from 'formik';

const name = legalAddress.addressLine;

<InputGroup>
            <Form.Control {
              ...getInputProps({
                placeholder: 'Search Places ...', name,
                autoComplete: name + Date.now()
              }
              )}
              isValid={
                getIn(touched, name) &&
                !getIn(errors, name)
              }
              isInvalid={
                getIn(touched, name) &&
                !!getIn(errors, name)
              }
            /> 
 </InputGroup>

如果有任何疑问,我很乐意回答。我不是formik专业人士,但是无论如何。

我花了很多时间来解决这个问题,希望这个答案对以后的人有所帮助。