React TypeScript、HooksForm、是的和 MaterialUI 自动完成

时间:2021-07-27 12:34:52

标签: reactjs typescript yup react-hook-form

我在 React Hook From 中使用 Yup 进行 MUI 自动完成验证时遇到问题。当我 concole.log 我的对象 (GrandLodge) 时,countryIDunionID 的道具总是 "0"

这是我的组件。

  • 首先是我控制的自动完成:
import { TextField, Typography } from "@material-ui/core";
import { Autocomplete } from "@material-ui/lab";
import React, { ChangeEvent } from "react";
import { Control, Controller, DeepMap, FieldError } from "react-hook-form";
import { Country, Union } from "./../../../service/client";
import { IBaseProps } from "./../Interfaces/BaseProps";
  Country,
  GrandLodge,
  Lodge,
  Union,
} from "./../../../service/client";
import { IBaseProps } from "./../Interfaces/BaseProps";

interface IHookFormSelectlistProps<T> extends IBaseProps {
  name: string;
  control: Control;
  errors?: DeepMap<Record<string, any>, FieldError>;
  disabled?: boolean;
  data: Array<T>;
  displayMember: (item: T) => string;
  valueMember: (item: T) => string;
  style?: React.CSSProperties;
  selectedValue?: any;
  onChange?: (item: T) => void;
  label?: string;
  className?: string;
  value: T | null;
}

interface IHookFormSelectlistState {
  setOpen: boolean;
}

type CustomSelect<T> = new (
  props: IHookFormSelectlistProps<T>
) => HookFormSelectList<T>;

class HookFormSelectList<T> extends React.Component<
  IHookFormSelectlistProps<T>,
  IHookFormSelectlistState
> {
  constructor(props: IHookFormSelectlistProps<T>) {
    super(props);

    this.state = {
      setOpen: false,
    };
  }

  private onInputChange = (
    e: ChangeEvent<{}>,
    value: string | T | null
  ): void => {
    if (this.props.onChange) {
      const item: T = this.props.data.toEnum().First((x: T) => x === value);
      this.props.onChange(item);
    }
  };

  render() {
    const body = () => (
      <React.Fragment>
        <Controller
          render={({ value, onBlur, onChange }) => (
            <div className={this.props.className}>
              <Autocomplete
                freeSolo
                fullWidth
                openOnFocus={true}
                onChange={(e: ChangeEvent<{}>, v: string | T | null) => {
                  onChange(e);
                  this.onInputChange(e, v);
                }}
                onOpen={() => this.setState({ ...this.state, setOpen: true })}
                onClose={() => this.setState({ ...this.state, setOpen: false })}
                getOptionSelected={(option, selected) => option === selected}
                getOptionLabel={this.props.displayMember}
                options={this.props.data}
                loading={false}
                renderInput={(params) => (
                  <TextField
                    {...params}
                    variant="outlined"
                    placeholder={this.props.label}
                  />
                )}
              />
            </div>
          )}
          defaultValue={this.props.value}
          name={this.props.name}
          control={this.props.control}
          onChangeName="onChange"
        />
        {this.props.errors && this.props.errors[this.props.name] && (
          <Typography variant="caption" color="error">
            <small>
              <span role="alert" id={`${this.props.name}Error`}>
                {this.props.errors[this.props.name].message}
              </span>
            </small>
          </Typography>
        )}
      </React.Fragment>
    );

    return body();
  }
}

export const CountriesSelect: CustomSelect<Country> = HookFormSelectList;
export const UnionSelect: CustomSelect<Union> = HookFormSelectList;
  • 我是的验证架构:
import * as Yup from "yup";

export const validationSchema = Yup.object().shape({
  name: Yup.string()
    .required("This field is required.")
    .min(2, "Lodge name has to be at last 2 charachters long.")
    .max(255, "Name can't be longer then 255 charachters."),
  countryId: Yup.string()
    .required("This field is required.")
    .min(0, "Country has to be selected."),
  email: Yup.string()
    .required("This field is required.")
    .email("Not a valid e-mail adress."),
  unionId: Yup.string()
    .required("This field is required.")
    .min(0, "Union has to be selected."),
  pypalAccount: Yup.string()
    .required("This field is required.")
    .min(2, "Pypal account has to be at last 2 charachters long.")
    .max(255, "Pypal account can't be longer then 255 charachters."),
  cryptoWallet: Yup.string()
    .required("This field is required.")
    .min(2, "Crypto wallet has to be at last 2 charachters long.")
    .max(255, "Crypto wallet can't be longer then 255 charachters."),
});
  • 我的 GrandLodge 对象:
export interface GrandLodge {
  time: Date;
  timeStamp: Date;
  readonly grandLodgeId: number;
  unionId: number;
  countryId: number;
  name: string | undefined;
  email: string | undefined;
  pypalAccount: string | undefined;
  cryptoWallet: string | undefined;
}
  • 还有我的组件:
import { yupResolver } from "@hookform/resolvers/yup";
import {
  Button,
  Container,
  CssBaseline,
  Grid,
  Typography,
} from "@material-ui/core";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import HookFormTextBox from "./../../../library/HookForm/Components/hookFomTextBox";
import { useStyles } from "./../../style.main";
import { validationSchema } from "./validationSchema";
import { Country, GrandLodge, Union } from "./../../../service/client";
import {
  CountriesSelect,
  UnionSelect,
} from "./../../../library/HookForm/Components/hookFormSelectlist";
import { withConnected } from "../../../state/withConnected";
import { WebAPI } from "./../../../service/webAPI";

interface IProps {
  countries: Country[];
}

const GrandLodgePageBase = (props: IProps): JSX.Element => {
  const [serverError, setServerError] = useState<string>("");
  const [unions, setUnions] = useState<Union[]>([]);

  useEffect(() => {
    const fetchData = async (): Promise<void> => {
      setUnions(await WebAPI.Union.getPage(0, 1000));
    };

    fetchData();
  }, []);

  const classes = useStyles();

  const { control, handleSubmit, errors, formState } = useForm<GrandLodge>({
    mode: "onChange",
    resolver: yupResolver(validationSchema),
  });

  const onSubmit = async (data: GrandLodge): Promise<void> => {
    if (!formState.isValid) {
      return;
    }

    console.log(data);
  };

  return (
    <Container component="main" maxWidth="xl">
      <CssBaseline />
      <Typography className={classes.title} variant="overline">
        Add new grand lodge
      </Typography>
      <form
        className={classes.form}
        onSubmit={handleSubmit((data: GrandLodge) => onSubmit(data))}
      >
        <Grid container className={classes.root} spacing={2}>
          <Grid item xs={12} sm={12} md={12} lg={6}>
            <HookFormTextBox
              name="name"
              type="text"
              label="Lodge Name"
              control={control}
              errors={errors}
              className={classes.formElement}
            />
          </Grid>
          <Grid item xs={12} sm={12} md={12} lg={6}>
            <HookFormTextBox
              name="email"
              type="email"
              label="E-mail"
              control={control}
              errors={errors}
              className={classes.formElement}
            />
          </Grid>
        </Grid>

        <Grid container className={classes.root} spacing={2}>
          <Grid item xs={12} sm={12} md={12} lg={6}>
            <CountriesSelect
              name="countryId"
              label="Country"
              displayMember={(x) => x.name!}
              valueMember={(x) => x.countryId.toString()}
              value={null}
              data={props.countries}
              control={control}
              errors={errors}
              className={classes.formElement}
            />
          </Grid>
          <Grid item xs={12} sm={12} md={12} lg={6}>
            <UnionSelect
              name="unionId"
              label="Union"
              displayMember={(x) => x.name!}
              valueMember={(x) => x.unionId.toString()}
              data={unions}
              value={null}
              control={control}
              errors={errors}
              className={classes.formElement}
            />
          </Grid>
        </Grid>

        <Grid container className={classes.root} spacing={2}>
          <Grid item xs={12} sm={12} md={12} lg={6}>
            <HookFormTextBox
              name="cryptoWallet"
              type="text"
              label="Crypto Wallet"
              control={control}
              errors={errors}
              className={classes.formElement}
            />
          </Grid>
          <Grid item xs={12} sm={12} md={12} lg={6}>
            <HookFormTextBox
              name="pypalAccount"
              type="text"
              label="Pypal Account"
              control={control}
              errors={errors}
              className={classes.formElement}
            />
          </Grid>
        </Grid>

        <Grid item xs={12} sm={12} md={12} lg={12} justify="center" container>
          <Button
            type="submit"
            variant="contained"
            color="primary"
            className={classes.submit}
            disabled={!formState.isValid}
          >
            SAVE
          </Button>
        </Grid>
        <Grid item md>
          <Typography variant="subtitle2" color="error" align="center">
            {serverError}
          </Typography>
        </Grid>
      </form>
    </Container>
  );
};

const GrandLodgePage = withConnected(GrandLodgePageBase, (s) => ({
  countries: s.state.countries,
}));

export default GrandLodgePage;

0 个答案:

没有答案