reactjs - why isn't refine()superrefine() working in zod with React Hook forms? - Stack Overflow

admin2025-04-18  4

I am making a react application using vite, react hook form (7.54.1) and zod (3.24.1) whose main function is a multi step form. Now in my form schemas, I need to customize the default zod messages using refine()/superrefine() but these methods just aren't working even when I provide guaranteed true condition to trigger them.

hardcoded sample data

const data = {
    "Expense": [
      {
        "accountingFieldCode": {
          "fieldCode": "DETAIL_CURRENCY_NAME",
          "fieldCodeName": "Currency Name",
          "description": null
        },
        "accountingOutputName": "sdfds",
        "defaultValueApplicable": "N",
        "defaultValue": "",
        "prefixValue": "",
        "suffixValue": "",
        "fieldCodeConcatRequired": "N",
        "concatCharacter": "",
        "fieldCodeToConcat": [
          {
            "fieldCode": "VENDOR_NAME",
            "fieldCodeName": "Vendor Name",
            "description": null
          },
          {
            "fieldCode": "EMPLOYEE_GL_CODE",
            "fieldCodeName": "Employee GL Code",
            "description": null
          }
        ],
        "maxDataLength": 3
      }
    ]
  };

schema

const defineFieldLineSchema = z.object({
  accountingFieldCode: z
    .object({
      fieldCode: z.string(),
      fieldCodeName: z.string(),
      description: z.string().nullable(),
    }),
  accountingOutputName: z.string().min(1, "Field is required"),
  defaultValueApplicable: z.string(),
  defaultValue: z.string().optional(),
  prefixValue: z.string().optional(),
  suffixValue: z.string().optional(),
  fieldCodeConcatRequired: z.enum(["Y", "N"]),
  concatCharacter: z.string(),
  fieldCodeToConcat: z.object({
    fieldCode: z.string(),
    fieldCodeName: z.string(),
    description: z.string().nullable(),
  }).optional(),
  maxDataLength: z.number(),
}).superRefine((data, ctx) => {
  if (data.fieldCodeConcatRequired === "Y" && !data.fieldCodeToConcat) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "fieldCodeToConcat is required when fieldCodeConcatRequired is 'Y'",
      path: ["fieldCodeToConcat"],
    });
  }
  if (Array.isArray(data.fieldCodeToConcat)) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "fieldCodeToConcat must be an object, not an array",
      path: ["fieldCodeToConcat"],
    });
  }
})

The only error I keep getting is the default zod message "Expected object, received array" instead of "fieldCodeToConcat must be an object, not an array" or "fieldCodeToConcat is required when fieldCodeConcatRequired is 'Y'".

Any help or suggestion regarding this would be greatly appreciated.

I am making a react application using vite, react hook form (7.54.1) and zod (3.24.1) whose main function is a multi step form. Now in my form schemas, I need to customize the default zod messages using refine()/superrefine() but these methods just aren't working even when I provide guaranteed true condition to trigger them.

hardcoded sample data

const data = {
    "Expense": [
      {
        "accountingFieldCode": {
          "fieldCode": "DETAIL_CURRENCY_NAME",
          "fieldCodeName": "Currency Name",
          "description": null
        },
        "accountingOutputName": "sdfds",
        "defaultValueApplicable": "N",
        "defaultValue": "",
        "prefixValue": "",
        "suffixValue": "",
        "fieldCodeConcatRequired": "N",
        "concatCharacter": "",
        "fieldCodeToConcat": [
          {
            "fieldCode": "VENDOR_NAME",
            "fieldCodeName": "Vendor Name",
            "description": null
          },
          {
            "fieldCode": "EMPLOYEE_GL_CODE",
            "fieldCodeName": "Employee GL Code",
            "description": null
          }
        ],
        "maxDataLength": 3
      }
    ]
  };

schema

const defineFieldLineSchema = z.object({
  accountingFieldCode: z
    .object({
      fieldCode: z.string(),
      fieldCodeName: z.string(),
      description: z.string().nullable(),
    }),
  accountingOutputName: z.string().min(1, "Field is required"),
  defaultValueApplicable: z.string(),
  defaultValue: z.string().optional(),
  prefixValue: z.string().optional(),
  suffixValue: z.string().optional(),
  fieldCodeConcatRequired: z.enum(["Y", "N"]),
  concatCharacter: z.string(),
  fieldCodeToConcat: z.object({
    fieldCode: z.string(),
    fieldCodeName: z.string(),
    description: z.string().nullable(),
  }).optional(),
  maxDataLength: z.number(),
}).superRefine((data, ctx) => {
  if (data.fieldCodeConcatRequired === "Y" && !data.fieldCodeToConcat) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "fieldCodeToConcat is required when fieldCodeConcatRequired is 'Y'",
      path: ["fieldCodeToConcat"],
    });
  }
  if (Array.isArray(data.fieldCodeToConcat)) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "fieldCodeToConcat must be an object, not an array",
      path: ["fieldCodeToConcat"],
    });
  }
})

The only error I keep getting is the default zod message "Expected object, received array" instead of "fieldCodeToConcat must be an object, not an array" or "fieldCodeToConcat is required when fieldCodeConcatRequired is 'Y'".

Any help or suggestion regarding this would be greatly appreciated.

Share Improve this question asked Jan 29 at 18:34 Shiladitya ThakurShiladitya Thakur 3402 gold badges7 silver badges20 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Issue is that superrefine() and refine() is checked only if the initial check is fulfilled. Meaning fieldCodeToConcat has to be an object first according to the condition

fieldCodeToConcat: z.object({
    fieldCode: z.string(),
    fieldCodeName: z.string(),
    description: z.string().nullable(),
  }).optional()

for fine grained validation, fieldCodeToConcat can be changed to a value that is expected to be true so that it goes on to refine() / superrefine() like below. The console log would get printed in this case.

const defineFieldLineSchema = z.object({
  fieldCodeToConcat: z.union([
    z.record(z.any()), // Accepts any object
    z.array(z.record(z.any())) // Accepts an array of any objects
  ]),
  
})
.superRefine((data, ctx) => {
  console.log("HELLO")
  if (Array.isArray(data.fieldCodeToConcat)) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "fieldCodeToConcat must be an object, not an array",
      path: ["fieldCodeToConcat"],
    });
  }
})
转载请注明原文地址:http://www.anycun.com/QandA/1744946311a89860.html