是的,根据 parent (parent.parent) 的兄弟使用不同的模式

Yup use different schema based on sibling of a parent (parent.parent)

在架构中,我想根据 parent 的 兄弟调整架构。

例如:如果 toggleMonday 为真,则工作日 -> 星期一应该有特定的验证模式。

现在下面的例子可以工作了。但是,它非常冗长。

const schema = yup.object().shape({
  toggleMonday: yup.bool().required(),
  toggleTuesday: yup.bool().required(),
  toggleWednesday: yup.bool().required(),
  toggleThursday: yup.bool().required(),
  toggleFriday: yup.bool().required(),
  toggleSaturday: yup.bool().required(),
  toggleSunday: yup.bool().required(),
  weekdays: yup.object()
   // works, toggleMonday is a sibling of weekdays
   .when('toggleMonday', {
        is: true,
        then: yup.object().shape({
          monday: yup.array().of(yup.object().shape(daySchema)).daySchemaFirstTimeslotRequired(),
          tuesday: yup.array().of(yup.object().shape(daySchema)),
          wednesday: yup.array().of(yup.object().shape(daySchema)),
          thursday: yup.array().of(yup.object().shape(daySchema)),
          friday: yup.array().of(yup.object().shape(daySchema)),
          saturday: yup.array().of(yup.object().shape(daySchema)),
          sunday: yup.array().of(yup.object().shape(daySchema)),
        }),
      })
      .when('toggleTuesday', {
        is: true,
        then: yup.object().shape({
          monday: yup.array().of(yup.object().shape(daySchema)),
          tuesday: yup.array().of(yup.object().shape(daySchema)).daySchemaFirstTimeslotRequired(),
          wednesday: yup.array().of(yup.object().shape(daySchema)),
          thursday: yup.array().of(yup.object().shape(daySchema)),
          friday: yup.array().of(yup.object().shape(daySchema)),
          saturday: yup.array().of(yup.object().shape(daySchema)),
          sunday: yup.array().of(yup.object().shape(daySchema)),
        }),
      })
      // etc.
});

如您所见,它非常重复。

本例中使用了mixed.when()方法。但是,您似乎只能定位同级或同级 children 字段。

将它分别嵌套在每一天下是行不通的,因为这些天嵌套在 'weekdays'.

行中的内容:

const schema = yup.object().shape({
  toggleMonday: yup.bool().required(),
  toggleTuesday: yup.bool().required(),
  toggleWednesday: yup.bool().required(),
  toggleThursday: yup.bool().required(),
  toggleFriday: yup.bool().required(),
  toggleSaturday: yup.bool().required(),
  toggleSunday: yup.bool().required(),
  weekdays: yup.object()
    // does not work, toggleMonday is not a sibling of 'monday'
    monday: yup.array().when('toggleMonday', {
      is: true,
      then: yup.array().of(yup.object().shape(daySchema)).daySchemaFirstTimeslotRequired(),
      otherwise: yup.array().of(yup.object().shape(daySchema)),
    }),
    tuesday: yup.array().when('toggleMonday', {
      is: true,
      then: yup.array().of(yup.object().shape(daySchema)).daySchemaFirstTimeslotRequired(),
      otherwise: yup.array().of(yup.object().shape(daySchema)),
    }),
    // etc.
});

有什么建议吗?

经过一番挖掘,我发现了这个 PR:https://github.com/jquense/yup/pull/201

似乎解决方法 (source) 是使用上下文:

// Pass the root data to context
schema.validateSync(data, {context: {data: data}})

// In your test, you can just access the root data
.test('myValidator', 'my validation message', function (value) {
  const data = this.options.context.data;
  // Do what you need to do with the root data
})

所以这意味着我应该使用上下文通过道具获取切换项,并在模式中使用它们。

与这个问题有点无关:我也在使用 formik 作为我的输入值。这仅通过 HOC withFormik (source)

支持上下文

我会坚持使用冗长的方法,因为实现会非常具体,由于实现细节的缘故,它不容易在我的应用程序中重复使用。

是的,给了你一个声明式 api,但你不限于像那样编写巨大的对象字面量。您的架构相当复杂,但如果没有其他方法可以更改它,请考虑以编程方式编写它 -

const weekdays =
  [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday' ]

const schema = yup.object().shape({
  toggleMonday: yup.bool().required(),
  toggleTuesday: yup.bool().required(),
  toggleWednesday: yup.bool().required(),
  toggleThursday: yup.bool().required(),
  toggleFriday: yup.bool().required(),
  toggleSaturday: yup.bool().required(),
  toggleSunday: yup.bool().required(),
  weekdays: weekdays.reduce(whenToggle, yup.object())
})

现在我们只需要执行 whenToggle -

const day =
  yup.object().shape(daySchema)

const title = (str = "") =>
  str.substr(0,1).toUpperCase() + str.substr(1)

const whenToggle = (y = {}, day = "") =>
  y.when
    ( `toggle${title(day)}`
    , { is: true, then: yup.object().shape(firstTimeslot(day)) }
    )

最后执行 firstTimeslot -

const dayList =
  yup.array().of(day)

const firstTimeslot = (day = "") =>
  weekdays.reduce
    ( (r, d) =>
        d === day
          ? { ...r, [d]: dayList.daySchemaFirstTimeslotRequired() }
          : { ...r, [d]: dayList }
    , {}
    )