reactjs 如何使用formik和yup验证执行数组字段的验证

pinkon5k  于 2023-02-18  发布在  React
关注(0)|答案(2)|浏览(322)

我在React JS中工作。我需要使用Formik和Yup对一个数组字段进行验证,如下所示。验证条件是这样一种方式,即输入时间不应等于或介于现有时间(已经输入的时间。例如:如果我输入14.05,它应该显示一个错误,因为输入时间已经在14.00(开始)和03.00(结束)之间)。我如何验证字段,我认为概念是清楚的。如果有任何疑问,请询问。

// API values
//home_delivery_monday_times: Array(3)
//0: {start: "09:00", end: "11:00"}
//1: {start: "14:00", end: "03:00 "}
//2: {start: "11:30", end: "13:00 "}
//length: 3
 <Formik
                            initialValues={formData}
                            enableReinitialize={true}
                            validationSchema={yup.object({
                                settings: yup.object({
                                    home_delivery_monday_times:
                                        yup.array().of(yup.object({ start: yup.string() })).test("is-valid", "The value shouldn't equal or between the existing", function (value) {
                                            return (
                                                // Validating conditions
                                            )
                                        })

                                })
                            })}
                            onSubmit={(values: any, { setSubmitting }) => {
                                console.log("values", values)
                            }}
                        >
                            {formik =>
.....}
{formik?.values?.settings?.home_delivery_monday_times?.map((item: any, index: any) => (
                                                                                <>
                                                                                    {index != 0 &&
                                                                                        <div className="p-col-12 p-sm-2 p-md-2" />
                                                                                    }
    
                                                                                    <div className="p-col-5 p-sm-4 p-md-4">
                                                                                        <label>Start</label>
                                                                                        <Field as={Calendar} value={item?.start ? new Date(`01-01-2000 ${item?.start}:00`) : ''} onSelect={(e: any) => { formik?.setFieldValue(`settings.home_delivery_monday_times.${index}.start`, moment(e?.value).format("HH:mm")) }} name={`settings.home_delivery_monday_times.${index}.start`} readOnlyInput={true} timeOnly />
                                                                                        <div className='p-col-12'>
                                                                                            <ErrorMessage name={`settings.home_delivery_monday_times.${index}.start`} component={FormErrorMsg} />
                                                                                        </div>
                                                                                    </div>
    
                                                                                    <div className="p-col-5 p-sm-4 p-md-4">
                                                                                        <label>End</label> <br />
                                                                                        <Field as={Calendar} value={item?.end ? new Date(`01-01-2000 ${item?.end?.trim()}:00`) : ''} onSelect={(e: any) => { formik?.setFieldValue(`settings.home_delivery_monday_times.${index}.end`, moment(e?.value).format("HH:mm")) }} name={`settings.home_delivery_monday_times.${index}.end`} readOnlyInput={true} timeOnly />
                                                                                    </div>
                                                                                    <div className="p-col-1 p-sm-2 p-md-2">
                                                                                        {index != 0 &&
                                                                                            <FiXCircle name="name" color="red" size="20px" onClick={() => formik?.values?.settings?.home_delivery_monday_times?.splice(index, 1)} />
                                                                                        }
                                                                                    </div>
                                                                                </>
    
                                                                            ))}
relj7zay

relj7zay1#

EDIT我建议使用moment库,然后您可以使用以下代码:

import * as Yup from "yup";
import { Formik, Form, Field, FieldArray, ErrorMessage } from "formik";
import moment from "moment";

export default function App() {
  const isValid = (timeSlots) => {
    if (!timeSlots) return;

    // compare each slot to every other slot
    for (let i = 0; i < timeSlots.length; i++) {
      const slot1 = timeSlots[i];

      if (!slot1.start || !slot1.end) continue;

      const start1 = moment(slot1.start, "HH:mm");
      const end1 = moment(slot1.end, "HH:mm");

      for (let j = 0; j < timeSlots.length; j++) {
        // prevent comparision of slot with itself
        if (i === j) continue;

        const slot2 = timeSlots[j];

        if (!slot2.start || !slot2.end) continue;
        const start2 = moment(slot2.start, "HH:mm");
        const end2 = moment(slot2.end, "HH:mm");

        if (
          start2.isBetween(start1, end1, undefined, "[]") ||
          end2.isBetween(start1, end1, undefined, "[]")
        ) {
          return `Overlapping time in slot ${j + 1}`;
        }
      }
    }
    // All time slots are are valid
    return "";
  };

  const handleSubmit = (values) => {
    console.log(values.mondayTimes);
  };

  return (
    <div className="container m-3">
      <Formik
        initialValues={{ mondayTimes: [{ start: "", end: "" }] }}
        onSubmit={handleSubmit}
        validationSchema={Yup.object().shape({
          mondayTimes: Yup.array()
            .of(
              Yup.object().shape({
                start: Yup.string().test("startTest", "Invalid Time", function (
                  value
                ) {
                  if (!value) return true;
                  return moment(value, "HH:mm").isValid();
                }),
                end: Yup.string().test("endTest", "Invalid Time", function (
                  value
                ) {
                  if (!value) return true;
                  if (!moment(value, "HH:mm").isValid()) {
                    return this.createError({ message: "Invalid Time" });
                  }

                  if (
                    moment(this.parent.start, "HH:mm").isSameOrAfter(
                      moment(this.parent.end, "HH:mm")
                    )
                  ) {
                    return this.createError({
                      message: "End time must be after start time"
                    });
                  }

                  return true;
                })
              })
            )
            .test("timesTest", "Error", function (value) {
              const message = isValid(value);
              return !message;
            })
        })}
        render={({ values, errors }) => (
          <Form>
            <FieldArray
              name="mondayTimes"
              render={(arrayHelpers) => (
                <div className="">
                  {values.mondayTimes.map((time, index) => (
                    <div className="row" key={index}>
                      <div className="col-5">
                        <div className="mb-3">
                          <label htmlFor="" className="form-label">
                            Start
                          </label>
                          <Field
                            className="form-control"
                            name={`mondayTimes.${index}.start`}
                          />
                          <ErrorMessage
                            className="form-text text-danger"
                            name={`mondayTimes.${index}.start`}
                          />
                        </div>
                      </div>
                      <div className="col-5">
                        <div className="mb-3">
                          <label htmlFor="" className="form-label">
                            End
                          </label>
                          <Field
                            className="form-control"
                            name={`mondayTimes.${index}.end`}
                          />
                          <ErrorMessage
                            className="form-text text-danger"
                            name={`mondayTimes.${index}.end`}
                          />
                        </div>
                      </div>

                      <div className="col-2 mt-4">
                        <button
                          className="btn btn-sm btn-danger m-2"
                          type="button"
                          onClick={() => arrayHelpers.remove(index)}
                        >
                          -
                        </button>
                      </div>
                    </div>
                  ))}

                  {isValid(values.mondayTimes)}

                  <button
                    className="btn btn-sm btn-primary m-2"
                    type="button"
                    onClick={() =>
                      arrayHelpers.insert(values.mondayTimes.length, {
                        start: "",
                        end: ""
                      })
                    }
                  >
                    +
                  </button>
                  <div>
                    <button className="btn btn btn-primary m-2" type="submit">
                      Submit
                    </button>
                  </div>
                </div>
              )}
            />
          </Form>
        )}
      />
    </div>
  );
}

下面是codesandbox,供您试用

oknwwptz

oknwwptz2#

虽然这篇文章已经很老了,但它作为我自己的可用性编辑组件的起点,对我帮助很大。
要回答这个问题,找出两个区间是否重叠是相当容易的。

const t1 = [1,3]
const t2 = [2,5]

如果我们有两个像上面这样的时间间隔,其中每个数字代表一个小时,我们可以使用下面的公式来确定它们是否重叠:

const isOverlapping = Math.max(1,2) <= Math.min(3,5)

简单来说这个等式是如果第一个间隔的结束时间在下一个间隔的开始时间之后,则间隔重叠。

baeldung的信用额度
现在,要在代码中实现这一点,我们需要使用上面的比较公式来比较所有的区间。

baeldung的信用额度
既然@Rishabh Singh提供了朴素方法的答案,我将提供一个修订版,做以下一些改进:
1.用更小、更现代的datefns库替换moment.js
1.用更快的扫描线方法O(n*log(n))替换简单比较O(n^2)
1.添加对一周中多天的支持
1.当开始/结束时间不是有效的hh:mm格式时,添加formik验证
1.当时隙不按升序排列时,添加formik验证
这是codesandbox链接

相关问题