6

我有以下组件,它制作一个表单并使用 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示例,还有这篇文章

如何使formik验证错误显示?

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

handleChange = address => {
    this.setState(()=>{
       this.props.form.setFieldValue('address',address);
       this.props.form.setFieldValue('latLng',{lat:null,lng:null})
       return {address};
    });
  };

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

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

Formik 在第 3,4 行之后没有反应,也许我没有完全理解 this.props.form.setFieldValue 是如何工作的,我认为 setFieldValue 会触发验证,我会进一步调查。

4

2 回答 2

2

可能晚了,但供将来参考.... ;-)

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

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

还需要一些工作。

目前,自动完成字段的结构包含值、地址和 lat/lng。

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

于 2018-11-08T09:51:16.457 回答
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 不专业,但无论如何。

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

于 2019-08-09T08:51:41.470 回答